template<typename T> T* Push(T* ptr);
template<typename T> T* Push(T& ref);
template<typename T, typename T1> T* Push(T1&& ref);
我有
int i = 0;
Push<int>(i);
但编译器称它不明确。那有多么暧昧?第二个功能显然是首选匹配,因为它更专业。特别是自从T1&amp;&amp;除非我明确转发/移动它,否则不会绑定到左值。
抱歉 - 我是一个int。否则,问题没有意义,我认为人们会推断它,因为它通常是循环迭代器。
答案 0 :(得分:7)
如果i
是一个int,那么第一个是不可行的。最后两个仍然存在然后,为了推导i
,第二个和第三个都产生相同的重载决策函数类型(int&
作为参数)。所以你必须依靠部分订购。
然而,部分排序无法区分它们。对于函数调用部分排序上下文,仅使用参数来确定顺序(并且不考虑示例中的返回类型),并且从它们剥离任何引用修饰符。因此,您将成功地在两个方向上相互推导出参数类型 - 两个参数类型将至少与其他参数一样专门化。并且都没有应用const,因此两者都不比另一个更专业。
有一个问题报告占位符,旨在澄清在部分订购期间与rvalue / lvalue参考困难相关的任何内容。有关详细信息,请参阅this usenet question。
如果两者中的任何一个应该更专业,我会说它应该是第一个。毕竟,它接受的参数少于另一个(另一个是潜在的完美转发器)。
特别是自T1&amp;&amp;除非我明确转发/移动它,否则不会绑定到左值。
实际上,它会接受任何东西。在模板中具有类型T&&
的参数将切换到“完美转发 - 演绎模式”,如果它是左值,则将T
推导到参数的类型,并添加左值-reference修饰符为T
类型,如果它是左值。因此,如果参数是左值,则生成的参数类型T& &&
会折叠为T&
,这会接受左值(就像您的情况一样)。
再看一下,你似乎想要做的是重载一个函数,通过移动它来获取对象。但由于T&&
的特殊推论(见下文),这不起作用。只需删除第一个函数并将代码编写为
template<typename T, typename T1> T* Push(T1&& ref) {
/* for lvalues, T1 is U& and rvalues it is U, with U being the
* argument type. */
T t1(std::forward<T1>(ref));
/* whatever needs to be done ... */
}
如果参数是rvalue,这将移动构造t1
,如果参数是左值,或者ref
没有移动构造函数,则复制T
。这只是一个例子,根据您的实际用例,它可能不是您实际应该做的。我也不确定为什么你有两个模板参数类型。我建议删除T
,并为返回类型说typename remove_reference<T1>::type *
。这样你就可以从论证演绎中获益。