什么是"表达SFINAE"?

xml*_*lmx 56 c++ templates sfinae visual-c++ c++11

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 <int I> struct A {};

char xxx(int);
char xxx(float);

template <class T> A<sizeof(xxx((T)0))> f(T){}

int main()
{
    f(1);
}
Run Code Online (Sandbox Code Playgroud)

例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
Run Code Online (Sandbox Code Playgroud)

我的问题是:什么是"表达SFINAE"?

Xeo*_*Xeo 70

我认为表达SFINAE在您链接的论文中得到了很好的解释.这是表达式上的SFINAE.如果里面的表达decltype无效,那么,从超负荷的贵宾休息室启动功能.您可以在本答案的最后找到规范性措辞.

对VC的注意事项++:他们没有实现它完全.在简单的表达方式上,它可能有用,但在其他方面,它不会.有关失败的示例,请参阅本答案评论中的讨论.为简单起见,这不起作用:

#include <iostream>

// catch-all case
void test(...)
{
  std::cout << "Couldn't call\n";
}

// 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\n";
}

// 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\n";
}

struct X{
  void f(){}
};

int main(){
  X x;
  test(x, &X::f);
  test(&x, &X::f);
  test(42, 1337);
}
Run Code Online (Sandbox Code Playgroud)

使用Clang,输出预期的:

可以使用引用
调用可以使用指针
调用无法调用

使用MSVC,我得到......好吧,编译错误:

1>src\main.cpp(20): error C2995: ''unknown-type' test(C,F)' : function template has already been defined
1>          src\main.cpp(11) : see declaration of 'test'

似乎GCC 4.7.1还不能完成任务:

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'

表达SFINAE的一个常见用途是在定义特征时,例如检查某个类是否具有某个成员函数的特征:

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)) {};
Run Code Online (Sandbox Code Playgroud)

实例.(令人惊讶的是,它再次在GCC 4.7.1上运行.)

另见我的这个答案,它在另一个环境中使用相同的技术(也就是没有特征).


规范性措辞:

§14.8.2 [temp.deduct]

p6 在模板参数推导过程中的某些点,有必要采用一个使用模板参数的函数类型,并用相应的模板参数替换这些模板参数.当任何显式指定的模板参数被替换为函数类型时,这在模板参数推导的开始处完成,并且当替换从默认参数推导或获得的任何模板参数时,再次在模板参数推断的结尾处完成.

p7 替换发生在函数类型和模板参数声明中使用的所有类型和表达式中.表达式不仅包括常量表达式如那些出现在数组边界或无类型模板参数而且一般表达式(即,非常量表达式)内部 sizeof,decltype和其它上下文允许非常量表达式.

p8如果替换导致无效的类型或表达式,则类型推导失败.如果使用替换参数写入,则无效的类型或表达式将是格式错误的.[...]

  • 实时示例链接已损坏:( (3认同)
  • 在 MSVC 2017 中编译良好。 (2认同)