【问题标题】:g++ and clang++ different behaviour with SFINAE and SFINAE failureg++ 和 clang++ SFINAE 和 SFINAE 失败的不同行为
【发布时间】:2016-04-12 15:41:36
【问题描述】:

几个 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&lt;X&gt; 类,它(通过 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.

我想这是对的 clang++,但我对 C++11 专家的第一个问题是:谁对? g++ 还是 clang++?

关于g++产生的程序输出,如下

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

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

现在稍微改一下:我是这样修改foo&lt;X&gt;::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 的情况下,我没有获得foo&lt;X&gt;::bar()"is int" 版本?

如果我在做一些愚蠢的事情,请耐心等待:我正在尝试学习 C++11。

对不起,我的英语不好。

【问题讨论】:

  • 您是否尝试过使用更新的 clang 版本(如 3.8)和更新的 gcc(如 5.3)?可能有指导意义...
  • 大部分最小化复制:coliru.stacked-crooked.com/a/713fbbbca7e8b8c5。我责怪缓存。
  • @T.C.哇,我完全忘记了“wtf 是 gcc 在做什么?”问题的一部分。很好的收获。
  • @Jesper Juhl - 不抱歉。是我当时可用的最新编译器吗?

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


【解决方案1】:

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

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

enableIfIsInt&lt;void&gt; 不在直接上下文中,这是因为X 不是int 的硬故障。您根本无法在该上下文中使用该表达式。

一旦你删除它 - 非模板 bar() 总是被调用。这是因为这两个函数是等效的匹配项,并且在重载决议中,非模板比模板更受欢迎。

所以真正的解决方案是使用标签调度:

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

【讨论】:

  • 我没有关注的是,在重载决议中,非模板优于模板;谢谢。
猜你喜欢
  • 2018-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-17
  • 2012-08-14
  • 2012-06-16
相关资源
最近更新 更多