什么是“表达SFINAE”?(What is “Expression SFINAE”?)

2019-06-18 09:30发布

在http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx ,在VC ++团队正式宣布,他们还没有实现的C ++ 11核心特性“表达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);
}

实施例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

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

Answer 1:

表达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);
}

铿锵,这种输出的预期:

可以参考调用
可以用指针来调用
无法调用

随着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)) {};

活生生的例子。 (其中,奇怪的是,再作用于GCC 4.7.1。)

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


规范的写法:

§14.8.2 [temp.deduct]

P6 在某些点在模板参数推导过程,有必要采取函数类型,使得使用的模板参数和与对应的模板参数替换这些模板的参数。 这是在模板参数推导的开始,当任何明确指定模板参数的模板实参推演结束代入函数式,并再次当推导出或默认参数获得任何模板参数代入完成。

P7 上的取代发生在所有类型和表达了在函数类型并在模板参数声明使用。 表达式不仅包括常量表达式如那些出现在数组边界或无类型模板参数而且 内部 一般表达式 (即,非常量表达式) sizeofdecltype ,和其它上下文允许非常量表达式。

P8如果在无效的类型或表达的取代产生,类型推导失败。 无效的类型或表达是一个将被形成不良的,如果使用取代的参数写入。 [...]



文章来源: What is “Expression SFINAE”?