g ++和clang ++不同的行为与SFINAE和SFINAE失败 [英] g++ and clang++ different behaviour with SFINAE and SFINAE failure
问题描述
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屋!