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
模板函数的返回类型评估为无效。
答案 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() ) )
bool( fun(v[0] ) )
并非实际评估,因为我们处于非评估上下文(decltype
,类似于{{ 1}})。这里重要的是,如果整个表达式被评估,将被评估,因此如果子表达式无效则整个表达式无效。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[]
,也会发生同样的情况。)