【问题标题】:How is type deduced from auto return type?如何从自动返回类型推导出类型?
【发布时间】:2012-08-02 10:29:24
【问题描述】:

This answer 有这样的代码 sn-p:

template<class T, class F>
auto f(std::vector<T> v, F fun)
    -> decltype( bool( fun(v[0] ) ), void() )
{
  // ...
}

它确实可以编译和工作 (at least on Ideone)。

那么,这种情况下的类型是如何推导出来的呢?

c++11 标准真的允许下一行吗?

decltype( bool( fun(v[0] ) ), void() )

我快速浏览了一下,它看起来无效。这种情况下ideone错了吗?


c++11 标准中的所有示例都是这样的,它们在 decltype 中都只有一种类型:

struct A {
  char g();
  template<class T> auto f(T t) -> decltype(t + g())
  { return t + g(); }
};

另一个例子:

void f3() {
  float x, &r = x;
  [=] {
  decltype(x) y1;
  decltype((x)) y2 = y1;
  decltype(r) r1 = y1;
  decltype((r)) r2 = y2;
};

还有一个

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = i;
decltype(i) x2;
decltype(a->x) x3;
decltype((a->x)) x4 = x3;

他们都在decltype中只有一个参数。上面的代码怎么会带两个参数(用逗号隔开)?


我创建了另一个示例(编译失败):

#include <vector>
#include <iostream>

template<class T, class F>
auto f(std::vector<T> v, F fun) -> decltype(bool(fun(v[0])), void())
{
  // ...
  (void)v;(void)fun;

  return fun(v.size());
}

void ops(int)
{
}

int main(){
  std::vector<int> v;
  f(v, [](int){ return true; });
  f(v,ops);
}

即使删除f(v,ops); 行,f 模板函数的返回类型也会被评估为 void。

【问题讨论】:

  • 为什么您认为它看起来无效?除了“它是有效的”之外,我真的不知道该回答什么会有所帮助。
  • @R.MartinhoFernandes 希望编辑后更清楚。问题是这里发生了什么:decltype( bool( fun(v[0] ) ), void() )

标签: c++ c++11 decltype


【解决方案1】:

decltype( bool( fun(v[0] ) ), void() ) 使用comma operator

分解,

bool( fun(v[0] ) ), void()

由两个表达式组成;第一个

bool( fun(v[0] ) )

被评估1并被丢弃,为整个表达式赋予值

void()

这是void 类型的值2

decltype 然后产生表达式的类型,如上所示为void

这里使用逗号操作符的原因是为了保证整个表达式只有在第一个子表达式有效时才有效;这是因为如果第一个子表达式无效,它会在 SFINAE 中用于将其排除在替换考虑之外。

这是可行的,因为尽管decltype 在语法上看起来像一个函数,但它实际上是一种语言结构(如sizeof)被定义为采用单个参数。用括号括起来可能更清楚 comma-operator 参数:

decltype( ( bool( fun(v[0] ) ), void() ) )

注意事项

  1. 表达式bool( fun(v[0] ) )实际上没有被评估,因为我们在一个未评估的上下文中decltype,类似于sizeof)。这里重要的是,如果整个表达式被评估,它将被评估,因此如果子表达式无效,那么整个表达式也是无效的。
  2. void() 真的不是一个值,但它在逗号运算符和 decltype 的上下文中表现得像一个值。

【讨论】:

  • 逗号的第一个操作数真的求值了吗?
  • @Kerrek 好吧,在这种情况下,什么都不会被评估。
  • @Kerrek:如果整个表达式不在未评估的上下文中(在这种情况下为decltype)。 ;)
  • @jrok:需要类型的地方(如std::function&lt;void()&gt;),它是一个函数类型。在函数参数中,它仍然是函数类型,但根据语言要求已转换为等效于 void (*)(),也就是函数指针类型。在需要表达式的地方,它是void 类型的“值”。哪个可以被滥用,比如here。 ;)
  • 我将 decltypesizeof 执行的“评估”称为“电子类型评估”:)
【解决方案2】:

decltype 产生括号之间的表达式的类型,不实际评估它(在接下来的部分中请记住这一点)。

, 运算符计算左参数/表达式,丢弃结果,计算右参数,并产生结果。因此,返回类型变为void

对于bool(fun(v[0])) 部分,这相当容易。 bool(f(...)) 根据调用 f 的结果构造一个临时的 bool。如果f 的返回类型不能转换为bool,这将触发一个错误,由于在decltype 内部(这称为"Expression SFINAE"),这将导致SFINAE。

f(v[0]) 会将v[0] 的返回值传递给f,其类型为T&amp;。如果f 没有T&amp; 可转换的参数,或者采用更多/更少的参数,这将触发错误,并再次导致SFINAE,原因与上述相同。

(如果std::vector 不支持operator[],也会发生同样的情况。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    相关资源
    最近更新 更多