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

Pup*_*ppy 2 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);
Run Code Online (Sandbox Code Playgroud)

我有

        int i = 0;
        Push<int>(i);
Run Code Online (Sandbox Code Playgroud)

但是编译器称它不明确.那有多么暧昧?第二个功能显然是首选匹配,因为它更专业.特别是因为T1 &&不会绑定到左值,除非我明确地转发/移动它.

对不起 - 我是个int.否则,问题没有意义,我认为人们会推断它,因为它通常是循环迭代器.

Joh*_*itb 7

如果i是int,那么第一个是不可行的.最后两个仍然存在 然后,为了扣除i,第二个和第三个都产生相同的过载分辨率的函数类型(两者都int&作为参数).所以你必须依靠部分订购.

但是,部分排序无法区分它们.对于函数调用部分排序上下文,仅使用参数来确定顺序(并且不考虑示例中的返回类型),并且从它们剥离任何引用修饰符.因此,您将成功地在两个方向上相互推导出参数类型 - 两个参数类型将至少与其他参数一样专门化.并且都没有应用const,因此两者都不比另一个更专业.

有一个问题报告占位符,旨在澄清在部分订购期间与rvalue/lvalue参考困难相关的任何内容.有关详细信息,请参阅此usenet问题.

如果两者中的任何一个应该更专业,我会说它应该是第一个.毕竟,它接受的争论少于另一个(另一个是潜在的完美转发器).


特别是因为T1 &&不会绑定到左值,除非我明确地转发/移动它.

实际上,它会接受任何东西.有类型的参数T&&中的模板将切换到"完美转发-演绎模式",这将推断T,如果它是一个右值参数的类型,并且左值参考改性剂添加的类型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 ... */
}
Run Code Online (Sandbox Code Playgroud)

t1如果参数是rvalue,这将移动构造,ref如果参数是左值,或者如果T没有移动构造函数,则复制.这只是一个例子,根据您的实际用例,它可能不是您实际应该做的.我也不确定为什么你有两个模板参数类型.我建议摆脱它T,并说typename remove_reference<T1>::type *回归类型.这样你就可以从论证演绎中获益.