什么是“表达 SFINAE"?
在 http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx,VC++团队正式声明他们尚未实现C++11核心特性Expression SFINAE".但是,以下代码示例复制自 http:///www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html 被 VC++ 编译器接受.
示例 1:
template 结构体{};字符 xxx(int);字符xxx(浮点数);模板A<sizeof(xxx((T)0))>f(T){}int main(){f(1);}
示例 2:
struct X {};结构体{Y(X){}};模板自动 f(T t1, T t2) ->声明类型(t1 + t2);//#1X f(Y, Y);//#2x x1, x2;X x3 = f(x1, x2);//推导在 #1 上失败(不能添加 X+X),调用 #2
我的问题是:什么是Expression SFINAE"?
解决方案Expression SFINAE 在您链接的论文中得到了很好的解释,我认为.它是关于表达式的 SFINAE.如果 decltype
中的表达式无效,那么,从重载的 VIP 休息室中踢出该函数.您可以在此答案的末尾找到规范性措辞.
关于 VC++ 的说明:他们没有完全实现它.在简单的表达式上,它可能有效,但在其他表达式上,它不会.有关失败的示例,请参阅对此答案的评论中的讨论.为简单起见,这行不通:
#include //包罗万象无效测试(...){std::cout <<无法调用
";}//当 C 是类引用类型且 F 是成员函数指针时捕获模板自动测试(C c, F f) ->decltype((c.*f)(), void())//'C' 是引用类型{std::cout <<可以调用参考
";}//当 C 是类指针类型并且 F 是成员函数指针时捕获模板自动测试(C c, F f) ->decltype((c->*f)(), void())//'C' 是指针类型{std::cout <<可以调用指针
";}结构 X{无效 f(){}};int main(){××;测试(x, &X::f);测试(&x, &X::f);测试(42、1337);}
使用 Clang,这会输出预期的内容:
<块引用>可以参考调用
可以用指针调用
无法呼叫
使用 MSVC,我得到......好吧,编译器错误:
<前>1>srcmain.cpp(20): error C2995: ''unknown-type' test(C,F)' : 函数模板已经定义1> srcmain.cpp(11) : 参见'test'的声明似乎 GCC 4.7.1 还不能胜任这项任务:
<前>source.cpp:替换'模板 decltype ((c.*f(), void())) test(C, F) [with C = X*;F = void (X::*)()]':source.cpp:29:17: 从这里需要source.cpp:11:6: 错误:无法将成员指针f"应用于非类类型X*"的c"source.cpp:替换'模板 decltype ((c.*f(), void())) test(C, F) [with C = int;F = 整数]':source.cpp:30:16: 从这里需要source.cpp:11:6: 错误:f"不能用作成员指针,因为它是int"类型Expression SFINAE 的一个常见用途是在定义特征时,例如检查类是否运行某个成员函数的特征:
struct has_member_begin_test{模板静态自动测试(U* p) ->decltype(p->begin(), std::true_type());模板<类>静态自动测试(...) ->std::false_type;};模板结构 has_member_begin: decltype(has_member_begin_test::test(0)) {};
现场示例.(令人惊讶的是,它再次在 GCC 4.7.1 上工作.)
另见我的这个答案,它在另一个环境(也就是没有特征)中使用了相同的技术.>
规范性措辞:
§14.8.2 [temp.deduct]
p6 在模板参数推导过程中的某些点,有必要采用使用模板参数的函数类型并将这些模板参数替换为相应的模板参数.这是在当任何明确指定的模板参数被替换到函数类型中时,模板参数推导的开始,当任何从默认参数中推导出或获得的模板参数被替换时,再次在模板参数推导结束.
p7 替换发生在函数类型和模板参数声明中使用的所有类型和表达式中.表达式不仅包括常量表达式,例如出现在数组边界中或作为非类型模板参数的表达式还包括一般表达式(即非常量表达式) sizeof
、decltype
和其他允许非常量表达式的上下文.
p8 如果替换导致无效的类型或表达式,则类型推导失败.如果使用替换参数编写,则无效类型或表达式是格式错误的类型或表达式.[...]
At http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx, the VC++ team officially declare that they have not yet implemented the C++11 core feature "Expression SFINAE". However, The following code examples copied from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html are accepted by the VC++ compiler.
example 1:
template <int I> struct A {};
char xxx(int);
char xxx(float);
template <class T> A<sizeof(xxx((T)0))> f(T){}
int main()
{
f(1);
}
example 2:
struct X {};
struct Y
{
Y(X){}
};
template <class T> auto f(T t1, T t2) -> decltype(t1 + t2); // #1
X f(Y, Y); // #2
X x1, x2;
X x3 = f(x1, x2); // deduction fails on #1 (cannot add X+X), calls #2
My question is: What is "Expression SFINAE"?
解决方案Expression SFINAE is explained quite well in the paper you linked, I think. It's SFINAE on expressions. If the expression inside decltype
isn't valid, well, kick the function from the VIP lounge of overloads. You can find the normative wording at the end of this answer.
A note on VC++: They didn't implement it completely. On simple expressions, it might work, but on others, it won't. See a discussion in the comments on this answer for examples that fail. To make it simple, this won't work:
#include <iostream>
// catch-all case
void test(...)
{
std::cout << "Couldn't call
";
}
// catch when C is a reference-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c.*f)(), void()) // 'C' is reference type
{
std::cout << "Could call on reference
";
}
// catch when C is a pointer-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c->*f)(), void()) // 'C' is pointer type
{
std::cout << "Could call on pointer
";
}
struct X{
void f(){}
};
int main(){
X x;
test(x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
With Clang, this outputs the expected:
Could call with reference
Could call with pointer
Couldn't call
With MSVC, I get... well, a compiler error:
1>srcmain.cpp(20): error C2995: ''unknown-type' test(C,F)' : function template has already been defined 1> srcmain.cpp(11) : see declaration of 'test'
It also seems that GCC 4.7.1 isn't quite up to the task:
source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = X*; F = void (X::*)()]': source.cpp:29:17: required from here source.cpp:11:6: error: cannot apply member pointer 'f' to 'c', which is of non-class type 'X*' source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = int; F = int]': source.cpp:30:16: required from here source.cpp:11:6: error: 'f' cannot be used as a member pointer, since it is of type 'int'
A common use of Expression SFINAE is when defining traits, like a trait to check if a class sports a certain member function:
struct has_member_begin_test{
template<class U>
static auto test(U* p) -> decltype(p->begin(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};
template<class T>
struct has_member_begin
: decltype(has_member_begin_test::test<T>(0)) {};
Live example. (Which, surprisingly, works again on GCC 4.7.1.)
See also this answer of mine, which uses the same technique in another environment (aka without traits).
Normative wording:
§14.8.2 [temp.deduct]
p6 At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
p7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside
sizeof
,decltype
, and other contexts that allow non-constant expressions.p8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...]
相关文章