g ++和clang ++不同的行为与SFINAE和SFINAE失败 [英] g++ and clang++ different behaviour with SFINAE and SFINAE failure

查看:175
本文介绍了g ++和clang ++不同的行为与SFINAE和SFINAE失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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>
使用enableIfIsInt
= typename std :: enable_if< std :: is_same< X,int> :: value,R&

public:
foo()
{}

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

void bar()
{
std :: cout< bar:is not int; is [< typeid(X).name()<< ] {
<< typeid(enableIfIsInt< void>)。name()< } \\\
;
}
};


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:在
中没有名为'type'的类型std :: __ 1 :: enable_if< false,void>'; 'enable_if'不能用于禁用
这个声明
= typename std :: enable_if< std :: is_same< X,int> :: value,R&
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:23:注意:在模板类型的实例化
alias'enableIfIsInt'请求这里
< typeid(enableIfIsInt< void>)。name()< } \\\
;
^
test.cpp:36:7:注意:在实例化成员函数时
'foo< long> :: bar'请求这里
fl.bar
^
生成了1个错误。

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



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



  bar:is not int; is [i] {v} 

因此g ++似乎忽略了现在有点改变:我修改 foo< X>的第二个版本:


$ b :bar()
以这种方式

  void bar()
{std :: cout < bar:is not int; is [< typeid(X).name()<< ] \\\
; }

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

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

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



如果我做一些愚蠢的话,要耐心等待我:学习C ++ 11。



对不起我的英语。

解决方案

clang的错误不是来自替换失败。它来自这里:

  void bar()
{
std :: cout< bar:is not int; is [< typeid(X).name()<< ] {
<< typeid(enableIfIsInt< void>)。name()< } \\\
; //< ==
}

enableIfIsInt< void& 不是在紧邻的上下文中,这是一个硬故障 X 不是 int 。你根本不能在那个上下文中使用那个表达式。



一旦你删除了 - 非模板 bar()总是被调用。这是因为两个函数都是等价的匹配,非模板在重载解析中优先于模板。



因此,真正的解决方案是使用标签调度:

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

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

void bar(std :: false_type){
std :: cout< bar:is not int; is [< typeid(X).name()<< ] \\\
;
}

这两个编译器都乐于产生:

  bar:is not int; is [l] 
bar:is int


A couple of questions for C++11 experts.

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.

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.

The program compile well with g++ 4.9.2 but clang++ 3.5.0 give the following error

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? g++ or clang++?

About the g++ produced program output, it's the following

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

so g++ seems to ignore the fl.bar(); instruction.

Now a little change: i modify the second version of foo<X>::bar() in this way

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

deleting the std::enable_if inside the function abomination. Now both g++ and clang++ are compiling without problems and the output, for both compiled versions of the program, is

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()?

Be patient with me if I'm doing some foolish: I'm trying to learn C++11.

And sorry for my bad English.

解决方案

clang's error isn't coming from the substitution failure. 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. You simply can't use that expression in that context.

Once you remove that - the non-template bar() is always called. 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:

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

这篇关于g ++和clang ++不同的行为与SFINAE和SFINAE失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆