带参考参数的重载函数模板

时间:2012-03-27 03:11:09

标签: c++ templates c++11 overloading

template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
int main()
{
    int x;
    f(x);   //ambiguous
}

为什么这个电话不明确?第一个模板专精是f <int>(int&amp;),第二个是f <int&>(int&amp;)。由于参数相同,因此根据偏序规则更加特殊的函数模板更好。然后根据标准14.8.2.4/9

  

对于给定类型,如果在两个方向上推导成功(即,上述转换后类型相同),并且P和A都是引用类型(在被上述类型替换之前):
   - 如果参数模板中的类型是左值引用而参数模板中的类型不是,则参数类型被认为比其他类型更专业; ......

第一个模板有T&amp;第二个是T&amp;&amp;,所以第一个应该更专业。这有什么不对?


编辑: 此代码在g ++ 4.6.1和VC ++ 2010 Express中进行了测试,两者都给出了模糊错误。

2 个答案:

答案 0 :(得分:5)

<强>准则

不要超载:

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

<强>原因

模式有一个特殊的模板推导规则:

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

此规则的存在是为了实现所谓的“完美转发”。它有助于bindmake_shared完美地转发他们的参数,同时保留cv限定符和“值类别”(左值/右值)。

这个特殊规则说,当使用左值参数(例如f(T&&))调用int时,该T被推导为左值引用(例如int&)而不是{{1 }}。并且对lvalue引用int的rvalue引用会折叠为对int的lvalue引用。即。

int

呼叫

f(x)

简化为:

f<int&>(int& && x);

修改

这不比f<int&>(int& x); 更专业或更不专业。

感谢Johannes Schaub的更正(见评论)。

<强>解决方案

您可以使用单一功能执行任何操作:

f<int>(int&)

如果template <typename T> void f(T&&) {} 推断为左值参考,请在第一次重载时执行您想要执行的操作,否则在第二次重载时执行任何操作:

T

并使用template <class T> void f_imp(T&, std::true_type) {std::cout << "lvalue\n";} template <class T> void f_imp(T&&, std::false_type) {std::cout << "rvalue\n";} template <typename T> void f(T&& x) { f_imp(std::forward<T>(x), std::is_lvalue_reference<T>()); } std::forward完美地转发给implementation-detail函数。

答案 1 :(得分:4)

您对该标准的解释似乎是正确的。

template <typename T> void f(T&) {}  // #1
template <typename T> void f(T&&) {} // #2

在#1中,T被成功推断为int,而在#2中,T被成功推断为int&,因此执行部分排序以选择打电话的功能。在调用f(x)的部分排序期间,将排序第一个(仅在这种情况下)参数的类型( [temp.deduct.partial] / 3 bullet 1)。在向两个方向扣除期间,类型P将为T,类型A将为表示T的合成类型( [temp.deduct.partial] / 5 ),因此扣除在两个方向都成功。

正如您所观察到的那样, [temp.deduct.partial] / 9 然后适用,并说#1的第一个参数更专业。因此,通过 [temp.deduct.partial] / 10 ,#1被选为最专业的模板,其专业化是重载解析的结果。

您没有提到您正在使用的编译器。我假设它是g ++ - 这似乎是该编译器中的一个错误(我测试了4.4.3和4.7之间的版本,他们都拒绝这个代码)。 clang接受您的代码,并按预期调用f(T &)重载。