简体   繁体   English

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

[英]g++ and clang++ different behaviour with SFINAE and SFINAE failure

A couple of questions for C++11 experts. 关于C ++ 11专家的几个问题。

I'm fighting with SFINAE and I came across a strange case in which g++ (4.9.2), and clang++ (3.5.0) behave differently. 我正在与SFINAE战斗,我遇到了一个奇怪的案例,其中g ++(4.9.2)和clang ++(3.5.0)表现不同。

I have prepared the following sample code. 我准备了以下示例代码。 I'm sorry but I'm unable to do it significantly more concise. 对不起,但我无法更明确地做到这一点。

#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;
 }

My idea was to create a template foo<X> class that (via SFINAE) can define a method in one or in another way depending on the X template argument. 我的想法是创建一个模板foo<X>类,通过SFINAE可以根据X模板参数以一种方式或另一种方式定义方法。

The program compile well with g++ 4.9.2 but clang++ 3.5.0 give the following error 该程序与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.

I suppose that is right clang++ but my first question to C++11 experts is: who right? 我想这是正确的铿锵++,但我对C ++ 11专家的第一个问题是:谁对吗? g++ or clang++? g ++或clang ++?

About the g++ produced program output, it's the following 关于g ++生成的程序输出,如下所示

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

so g++ seems to ignore the fl.bar(); 所以g ++似乎忽略了fl.bar(); instruction. 指令。

Now a little change: i modify the second version of foo<X>::bar() in this way 现在稍微改变一下:我用这种方式修改了foo<X>::bar()的第二个版本

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

deleting the std::enable_if inside the function abomination. 删除函数憎恶中的std::enable_if Now both g++ and clang++ are compiling without problems and the output, for both compiled versions of the program, is 现在g ++和clang ++都在编译时没有问题,并且对于程序的两个编译版本的输出都是

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

So, my second question is: what I'm doing wrong? 所以,我的第二个问题是:我做错了什么? Why, in the int case, I don't obtain the "is int" version of foo<X>::bar() ? 为什么,在int情况下,我没有获得foo<X>::bar()"is int"版本?

Be patient with me if I'm doing some foolish: I'm trying to learn C++11. 如果我做得有些愚蠢,请耐心等待我:我正在努力学习C ++ 11。

And sorry for my bad English. 抱歉我的英语不好。

clang's error isn't coming from the substitution failure. clang的错误不是来自替换失败。 It's coming from here: 它来自这里:

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

enableIfIsInt<void> isn't in the immediate context, that's a hard failure for X is not int . enableIfIsInt<void>不在直接上下文中,这是X的硬故障不是int You simply can't use that expression in that context. 你根本无法在该上下文中使用该表达式。

Once you remove that - the non-template bar() is always called. 删除后 - 始终调用非模板bar() That's because both functions are equivalent matches and non-templates are preferred to templates in overload resolution. 这是因为两个函数都是等效匹配,非模板比重载解析中的模板更受欢迎。

So the real solution is to use tag-dispatching: 所以真正的解决方案是使用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";
}

with which both compilers happily yield: 两个编译器愉快地产生:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 g ++和clang ++使用变量模板和SFINAE的不同行为 - g++ and clang++ different behaviour with variable template and SFINAE 使用clang ++和g ++的SFINAE和CRTP有些神奇 - Some magic with SFINAE and CRTP using clang++ and g++ C ++朋友函数模板重载和SFINAE在clang ++,g ++,vc ++中的不同行为(C ++ 14模式) - C++ friend function template overloading and SFINAE different behaviors in clang++, g++, vc++ (C++14 mode) g ++和clang ++使用静态成员的递归初始化的不同行为 - g++ and clang++ different behaviour with recursive initialization of a static member g ++和clang ++使用运算符&lt;()重载的不同行为 - g++ and clang++ different behaviour with operator<() overloading g ++和clang ++使用指向可变参数模板函数的指针的不同行为 - g++ and clang++ different behaviour with pointer to variadic template functions g ++和clang ++使用自动参数的模板特化的不同行为 - g++ and clang++ different behaviour with template specialization for auto argument g++ 和 clang++ 与可变参数容器的不同行为 - g++ and clang++ different behaviour with variadic container 带有集成模板参数的g ++和clang ++不同的行为 - g++ and clang++ different behaviour with integral template parameter g ++和clang ++与流输入和无符号整数的不同行为 - g++ and clang++ different behaviour with stream input and unsigned integer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM