g ++和clang ++与SFINAE和SFINAE失败的不同行为

时间:2016-04-12 15:41:37

标签: c++ c++11 g++ sfinae clang++

C ++ 11专家的几个问题。

我正在与SFINAE战斗,我遇到了一个奇怪的案例,其中g ++(4.9.2)和clang ++(3.5.0)表现不同。

我准备了以下示例代码。对不起,但我无法更明确地做到这一点。

#include <string>
#include <iostream>
#include <typeinfo>
#include <type_traits>

template <typename X>
class foo
 {
   private:
      template <typename R>
         using enableIfIsInt
         = typename std::enable_if<std::is_same<X, int>::value, R>::type;

   public:
      foo ()
       { }

      template <typename R = void>
         enableIfIsInt<R> bar ()
          { std::cout << "bar: is int\n"; }

      void bar ()
       {
         std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
            << typeid(enableIfIsInt<void>).name() << "}\n";
       }
 };


int main ()
 {
   foo<long>  fl;
   foo<int>  fi;

   fl.bar();
   fi.bar();

   return 0;
 }

我的想法是创建一个模板foo<X>类,通过SFINAE可以根据X模板参数以一种或另一种方式定义方法。

程序使用g ++ 4.9.2很好地编译,但是clang ++ 3.5.0会出现以下错误

test.cpp:13:36: error: no type named 'type' in
      'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable
      this declaration
         = typename std::enable_if<std::is_same<X, int>::value, R>::type;
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:23: note: in instantiation of template type
      alias 'enableIfIsInt' requested here
            << typeid(enableIfIsInt<void>).name() << "}\n";
                      ^
test.cpp:36:7: note: in instantiation of member function
      'foo<long>::bar' requested here
   fl.bar();
      ^
1 error generated.

我认为这是正确的铿锵++但我对C ++ 11专家的第一个问题是:谁对? g ++或clang ++?

关于g ++生成的程序输出,它是以下

bar: isn't int; is [i]{v}

所以g ++似乎忽略了fl.bar();指令。

现在稍加改动:我以这种方式修改foo<X>::bar()的第二个版本

  void bar ()
   { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; }

删除功能憎恶中的std::enable_if。现在g ++和clang ++都编译没有问题,并且对于程序的两个编译版本的输出都是

bar: isn't int; is [l]
bar: isn't int; is [i]

所以,我的第二个问题是:我做错了什么?为什么,在int案例中,我没有获得"is int"版本的foo<X>::bar()

如果我做傻话,请耐心等待我:我正在努力学习C ++ 11。

抱歉我的英语不好。

1 个答案:

答案 0 :(得分:6)

clang的错误并非来自替换失败。它来自这里:

  void bar ()
   {
     std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
        << typeid(enableIfIsInt<void>).name() << "}\n"; // <==
   }

enableIfIsInt<void>并非直接背景,X的严重失败不是int。你根本无法在该上下文中使用该表达式。

删除后 - 始终调用非模板bar()。这是因为两个函数都是等效匹配,非模板比重载解析中的模板更受欢迎。

所以真正的解决方案是使用tag-dispatching:

void bar() { bar(std::is_same<X, int>{}); }

void bar(std::true_type ) {
    std::cout << "bar: is int\n";
}

void bar(std::false_type ) {
    std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n";
}

两个编译器都很高兴地产生:

bar: isn't int; is [l]
bar: is int