有人可以帮我理解为什么以下代码
1)不会导致error: redefinition of 'foo'
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 );
}
答案 0 :(得分:2)
您错误地使用了std::enable_if_t
。启用的定义也必须与主模板匹配。因此,您可以在返回类型上启用enable_if。不出所料,这个定义现在含糊不清。
#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 );
}
为什么你不仅仅为foo
和A&&
重载A
?
答案 1 :(得分:2)
这两个问题的答案是&#34;因为您没有正确使用enable_if_t
&#34;。
模式是
template< typename A_t,
typename = std::enable_if_t< std::is_same<A_t, A&>::value > >
注意:
typename =
引入了一个未命名的类型模板参数。 ::value
,而非::type
才能传递给std::enable_if_t
。 使用正确的模式后,第二个和第三个定义会出现重新定义错误(因为单独的默认模板参数不能重载)。
对于现在的代码,第二个模板参数的替换总是失败,因为std::enable_if_t
想要一个非类型的布尔参数,而std::is_same<A_t, A&>::type
是一个类型。由于这些模板没有有效的实例化,因此行为未定义。
答案 2 :(得分:1)
编译器更喜欢:
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;
}
因为后两个重载会引入non-deduced context。
正如其他答案中所述,std::enable_if
的应用不正确。要应用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;
}
现在,如果您在上述其中一个选项中更改了代码,则会出现歧义编译错误,导致重载解析无法在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&&)
之间进行选择。< / p>
一旦你摆脱了模糊的调用(即摆脱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&& )
:
在您的情况下,a
是lvalue
,因此A_t
将被推断为A&
,并且由于SFINAE,首选超负荷是首选。