如何从自动退货类型中推断出类型?

时间:2012-08-02 10:29:24

标签: c++ c++11 decltype

This answer有一个这样的代码段:

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() )

我快速浏览了一下,看起来并不合适。在这种情况下,意识错误吗?


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模板函数的返回类型评估为无效。

2 个答案:

答案 0 :(得分:14)

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)被定义为采用单个参数。将逗号运算符参数括起来可能更清楚:

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

注释

  1. 表达式bool( fun(v[0] ) )并非实际评估,因为我们处于非评估上下文decltype,类似于{{ 1}})。这里重要的是,如果整个表达式被评估,将被评估,因此如果子表达式无效则整个表达式无效。
  2. sizeof不是真正一个值,但它的行为类似于逗号运算符和void()上下文中的值。

答案 1 :(得分:7)

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

,运算符计算左参数/表达式,抛出结果,计算正确的参数,并产生该结果。因此,返回类型变为void

对于bool(fun(v[0]))部分,它相当容易。 bool(f(...))根据调用f的结果构造一个临时bool。如果f的返回类型无法转换为bool,则会触发错误,这会导致SFINAE位于decltype内(这称为{{3} }})。

f(v[0])会将v[0]的返回值传递给f,其类型为T&。如果f没有T&可转换为的参数,或者接受更多/更少的参数,则会触发错误,同样会导致SFINAE出于与上述相同的原因

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