[英]Why does enable_if not behave as expected?
Can someone please help me to understand why the following code 有人可以帮我了解以下代码的原因
1) does not cause an error: redefinition of 'foo'
1)不会导致
error: redefinition of 'foo'
2) why it outputs T
and not A&
2)为什么输出
T
而不输出A&
#include <type_traits>
#include <iostream>
class A{};
template< typename T >
void foo( T&& )
{
std::cout << "T" << std::endl;
}
template< typename A_t, std::enable_if_t< std::is_same<A_t, A&>::type > >
void foo( A_t&& )
{
std::cout << "A&" << std::endl;
}
template< typename A_t, std::enable_if_t< std::is_same<A_t, A>::type > >
void foo( A_t&& )
{
std::cout << "A" << std::endl;
}
int main()
{
A a;
foo( a );
}
You are using std::enable_if_t
incorrectly. 您使用的
std::enable_if_t
错误。 Also the enabled definitions have to match the primary template. 同样,启用的定义必须匹配主模板。 Therefore you could enable_if on the return type.
因此,您可以在返回类型上启用enable_if。 Unsurprisingly the definition is ambiguous now.
毫不奇怪,现在的定义是模棱两可的。
#include <iostream>
#include <type_traits>
class A {};
template< typename T >
void foo( T&& )
{
std::cout << "T" << std::endl;
}
template< typename A_t >
std::enable_if_t< std::is_same<A_t, A&>::value >
foo( A_t&& )
{
std::cout << "A&" << std::endl;
}
template< typename A_t >
std::enable_if_t< std::is_same<A_t, A>::value >
foo( A_t&& )
{
std::cout << "A" << std::endl;
}
int main()
{
A a;
foo( a );
}
Why are you not just overloading foo
for A&&
and A
? 为什么您不只是为
A&&
和A
重载foo
?
The answer to both questions is "because you are not using enable_if_t
correctly". 这两个问题的答案是“因为您没有正确使用
enable_if_t
”。
The pattern is 模式是
template< typename A_t,
typename = std::enable_if_t< std::is_same<A_t, A&>::value > >
Notes: 笔记:
typename =
introduces an unnamed type template parameter. typename =
引入未命名的类型模板参数。 ::value
, not ::type
to pass to std::enable_if_t
. ::value
,而不是::type
才能传递给std::enable_if_t
。 Once you use the correct pattern, you get a redefinition error for the second and third definitions (because you cannot overload on a default template parameter alone). 一旦使用了正确的模式,就会对第二个和第三个定义重新定义错误(因为您不能仅在默认模板参数上重载)。
For the code as it stands now, the substitution always fails for the second template parameter, because std::enable_if_t
wants a non-type boolean argument and std::is_same<A_t, A&>::type
is a type. 对于现在的代码,第二个模板参数的替换总是失败,因为
std::enable_if_t
想要一个非类型的布尔参数,而std::is_same<A_t, A&>::type
是一个类型。 Since there are no valid instantiations of these templates, the behaviour is undefined. 由于这些模板没有有效的实例,因此行为是不确定的。
The compiler prefers: 编译器更喜欢:
template< typename T >
void foo( T&& )
{
std::cout << "T" << std::endl;
}
Over: 过度:
template< typename A_t, std::enable_if_t< std::is_same<A_t, A&>::type>>
void foo( A_t&& )
{
std::cout << "A&" << std::endl;
}
and 和
template< typename A_t, std::enable_if_t< std::is_same<A_t, A>::type>>
void foo( A_t&& )
{
std::cout << "A" << std::endl;
}
Because the later two overloads introduce non-deduced context . 因为后面的两个重载引入了非推导上下文 。
As already being stated in other answers, the application of std::enable_if
is not correct. 正如其他答案中已经提到的那样,
std::enable_if
的应用是不正确的。 In order to apply SFINAE you have to alter your code in one of the following ways: 为了应用SFINAE,您必须通过以下方式之一更改代码:
template< typename A_t, std::enable_if_t< std::is_same<A_t, A&>::value>* = nullptr>
void foo( A_t&& )
{
std::cout << "A&" << std::endl;
}
template< typename A_t, std::enable_if_t< std::is_same<A_t, A>::value>* = nullptr>
void foo( A_t&& )
{
std::cout << "A" << std::endl;
}
template< typename A_t>
std::enable_if_t< std::is_same<A_t, A&>::value>
foo( A_t&& )
{
std::cout << "A&" << std::endl;
}
template< typename A_t>
std::enable_if_t< std::is_same<A_t, A>::value>
foo( A_t&& )
{
std::cout << "A" << std::endl;
}
Now once you've alter your code in one of the options illustrated above, you'll get an ambiguity compile error cause overload resolution cannot choose between template<typename A_t, std::enable_if_t<std::is_same<A_t, A&>::value>* = nullptr> void foo(A_t&&)
and template<typename T> void foo(T&&)
. 现在,一旦您在上面说明的选项之一中更改了代码,就会得到歧义的编译错误,导致重载解析无法在
template<typename A_t, std::enable_if_t<std::is_same<A_t, A&>::value>* = nullptr> void foo(A_t&&)
和template<typename T> void foo(T&&)
Once you get rid of the ambiguous call (ie, get rid of template<typename T> void foo( T&& )
) the code will compile and run and template< typename A_t, std::enable_if_t< std::is_same<A_t, A&>::value>> void foo( A_t&& )
is going to be choosen due to forwarding reference collapsing rules: 一旦摆脱了模棱两可的调用(即摆脱了
template<typename T> void foo( T&& )
),代码将编译并运行,并且template< typename A_t, std::enable_if_t< std::is_same<A_t, A&>::value>> void foo( A_t&& )
将由于转发引用折叠规则而被选择:
In your case a
is an lvalue
thus A_t
will be deduced to A&
and bulls-eye first overload is preferred due to SFINAE. 在您的情况下,
a
是一个lvalue
因此将A_t
推导为A&
并且由于SFINAE的原因,首选牛眼式第一重载。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.