简体   繁体   English

为什么enable_if的行为不符合预期?

[英]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: 笔记:

  1. typename = introduces an unnamed type template parameter. typename =引入未命名的类型模板参数。
  2. You need ::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,您必须通过以下方式之一更改代码:

Option 1: 选项1:

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

Option 2: 选项2:

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&& )将由于转发引用折叠规则而被选择:

  • A& & becomes A& A&&成为A&
  • A& && becomes A& A&&&成为A&
  • A&& & becomes A& A &&&成为A&
  • A&& && becomes A&& A && &&成为A &&

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM