在MSVC2015中,SFINAE成员检测的输出错误

时间:2016-09-16 16:37:21

标签: c++ templates sfinae

我正在学习SFINAE以及如何使用void_t轻松实现它。但是我为不同的编译器得到了不同的输出:

//pre c++17 void_t definition:
template<class... Ts> struct make_void {typedef void type;};
template<class... Ts> using void_t = typename make_void<Ts...>::type;

//check for member helper structures
template<class, class = void>
struct has_abc : std::false_type
{ };

template<class T>
struct has_abc<T, void_t<decltype(T::abc)>> : std::true_type
{ };

class has
{
public:
    void abc();
};

class has_not
{ };

int main()
{
    std::cout << has_abc<has>::value << std::endl;
    std::cout << has_abc<has_not>::value << std::endl;
}

GCC 5.3.0打印预期输出1 0,但MSVC 2015打印0 0,为什么?

编辑:

使用可能违反c ++语法的GCC 5.3.0代码的附加示例:

template<class T>
void test()
{
    std::cout << std::is_same<decltype(T::func), void(T::*)(void)>::value << std::endl;
}

class Test
{
public:
    void func();
};

int main()
{
    test<Test>();
}

输出:

1

2 个答案:

答案 0 :(得分:2)

实际上您的代码存在错误。 MSVC是正确的,而GCC是完全错误的。

指向成员函数的指针的语法并不像那样工作。您 &放在表达式前面:

//check for member helper structures
template<class, class = void>
struct has_abc : std::false_type {};

template<class T>
struct has_abc<T, void_t<decltype(&T::abc)>> : std::true_type {};
//     Notice the '&' there ------^

T::member的语法仅适用于静态数据成员,typename T::member适用于成员类型。在与sfinae合作时,区分小语法属性和差异非常重要。

作为评论中的请求,这里有多个语句,表明如果没有& GCC 5.3,则无法引用非静态成员:https://godbolt.org/g/SwmtG2

以下是GCC的一个示例:http://coliru.stacked-crooked.com/a/0ee57c2c34b32753

以下是MSVC的示例:http://rextester.com/FJH22266

这里是C ++标准中明确声明如果没有&,表达式不完整的部分:

[expr.prim.id]/2

  

表示非静态数据成员或类的非静态成员函数的id表达式只能用于:

     
      
  • 作为类成员访问([expr.ref])的一部分,其中对象表达式引用成员的类或从该类派生的类,

  •   
  • 或形成指向成员的指针([expr.unary.op])或

  •   
  • 如果该id-expression表示非静态数据成员,并且它出现在未评估的操作数中。 [例如:

  •   

-

 struct S {
   int m;
 };

 int i = sizeof(S::m);           // OK
 int j = sizeof(S::m + 42);      // OK
     

- 结束示例]

正如我们在聊天中讨论的那样,我们得出的结论是,两个编译器存在差异的原因是GCC和MSVC都存在阻止此代码正常工作的错误。如上所述,如果某个不相关的课程没有正确实施该规则,MSVC将拒绝应用SFINAE规则:http://rextester.com/FGLF68000

请注意,有时候,更改类型特征的名称有助于MSVC正确解析我的代码,但它几乎不可靠。

如果您希望代码按预期工作,请考虑向微软报告错误并升级您的GCC版本。

答案 1 :(得分:0)

这种方式有效:

template<class T>
struct has_abc<T, void_t<decltype(std::declval<T>().abc())>> : std::true_type
{ };