C ++模板重载 - 调用了错误的函数

时间:2010-06-06 15:22:34

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

        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。否则,问题没有意义,我认为人们会推断它,因为它通常是循环迭代器。

1 个答案:

答案 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 *。这样你就可以从论证演绎中获益。