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中进行了测试,两者都给出了模糊错误。
答案 0 :(得分:5)
<强>准则强>:
不要超载:
template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
<强>原因强>:
模式有一个特殊的模板推导规则:
template <typename T> void f(T&&) {}
此规则的存在是为了实现所谓的“完美转发”。它有助于bind
和make_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 &)
重载。