当lvalue-ref-arg和rvalue-ref-arg作为相同的forwarding-ref类型传递时,为什么它不起作用?

时间:2017-02-23 08:45:04

标签: c++ c++11 templates overloading forwarding-reference

#include <type_traits>

template<typename T>
void f(T&& a, T&& b)
{}

int main()
{
    int n;
    f(n, std::move(n));
}

T&&是转发引用类型,因此我认为decltype(a)应该是int&decltype(b)应该是int&&

但是,上面的代码会生成以下错误:

  

main.cpp(13,2):错误:没有匹配函数来调用'f'            f(n,std :: move(n));

     

main.cpp(7,6):注意:候选模板被忽略:推导出参数'T'的冲突类型('int&amp;'与'int')

     

void f(T&amp; a,T&amp;&amp; b)

     

生成了1个错误。

当lvalue-ref-arg和rvalue-ref-arg作为相同的forwarding-ref类型传递时,为什么它不起作用?

我的编译器是clang 4.0。

2 个答案:

答案 0 :(得分:4)

编译错误非常简单,Tint&无法同时推断int。为了使其工作,您必须提供额外的模板参数:

template<typename T1, typename T2>
void f(T1&& a, T2&& b)
{}

推理

模板参数推导在处理基于forwarding references

reference collapsing rules(a.k.a通用引用)时遵循特殊规则
  • U&安培; &安培;成为U&amp;
  • U&安培; &安培;&安培;成为U&amp;
  • U&安培;&安培; &安培;成为U&amp;
  • U&安培;&安培; &安培;&安培;成为U&amp;&amp;

例如,如果您有一个功能模板:

template<typename T>
void foo(T&&) {}
  1. 如果foo输入是U类型的左值,T将被扣除到U&。遵循参数折叠规则,参数类型将变为U&
  2. 如果foo输入是U类型的右值,T将被扣除到U,因此参数类型将为U&&
  3. 现在按照上面的推理,模板参数推导会将T推导出int&作为第一个参数,因为输入是lvalue

    模板参数推导将尝试匹配第二个参数的类型,但对于第二个参数,因为输入是遵循上述T规则的右值,将推导到int

    此时,编译器向空中举手并尖叫“ dude T的扣除类型必须与所有输入参数匹配”。

答案 1 :(得分:2)

当使用转发参考(1)工作到非表面深度时,重要的是要了解它们的实际工作方式,而不是将它们视为“神奇”。

转发参考的所有“魔力”都是标准中的这一裁决:

当参数类型T &&发生类型推导时,其中T是推导类型,并且U类型的左值被用作参数,类型U &是用于类型推导而不是U

我们来看这个例子:

template <class T>
void foo(T&& a);

int i = 42;

致电foo(42)时会发生什么:42int类型的左值。因此,T将推断为inta的类型将为int &&,这是对int的右值引用。

致电foo(i)时会发生什么:iint类型的左值。由于转发参考规则,将发生扣除,就好像int &是类型一样。因此T推导为int &;因此,a的类型为“int & &&”,该引用会折叠为int &

考虑到这一点,很明显为什么你的例子失败了。同样的T必须推断为int &(因为n)和int(因为std::move(n))。很自然地,演绎失败了。

如果您希望每个参数都是自己的转发参考,则每个参数需要一个单独的模板参数:

template<class T, class U>
void f(T&& a, U&& b)
{}

(1)请注意,根据已被工作文件接受的提案N4164,首选术语是转发参考。