c++ - 可变参数模板 : lvalue vs rvalue references

标签 c++

以下代码使用 c++11 或更高版本编译

#include <iostream>
#include <cstdlib>
#include <cstdio>

#define p 1

// p1: prototype 1
template <class Function, class... Args>
void addv(Function&& f, Args&... args) {
  std::cout << f(args...) << std::endl;
}
// p2: prototype 2
template <class Function, class... Args>
void addv(Function& f, Args&... args) {
  std::cout << f(args...) << std::endl;
}

int add(int& a, int& b) {
  return a+b;
}

class Adder {
  public:
    int operator () (int& a, int&b) {
      return a+b;
    }
};

int main() {
  int a = 2;
  int b = 1;
  Adder adder;

  addv<int (int&,int&),int,int>(add,a,b); // uses p1 OR p2
  addv<Adder,int,int>(Adder(),a,b); // uses p1
  addv<Adder,int,int>(adder,a,b); // uses p2

}

如果原型(prototype) 2 被移除,并且它被编译,则会发生以下错误:
exp.cpp:36:36: error: no matching function for call to ‘addv<Adder, int, int>(Adder&, int&, int&)’
       addv<Adder,int,int>(adder,a,b); // uses p2
                                    ^
exp.cpp:9:10: note: candidate: template<class Function, class ... Args> void addv(Function&&, Args& ...)
     void addv(Function&& f, Args&... args) {
          ^~~~
exp.cpp:9:10: note:   template argument deduction/substitution failed:
exp.cpp:36:36: note:   cannot convert ‘adder’ (type ‘Adder’) to type ‘Adder&&’
       addv<Adder,int,int>(adder,a,b); // uses p2
                                    ^

为什么adder无法从左值隐式转换为右值,因为需要将行 addv<Adder,int,int>(adder,a,b);使用原型(prototype) 1?

是否可以显式创建 adder 的右值引用?让它正确匹配原型(prototype) 1?

最佳答案

它不能被转换,因为在函数签名中使用右值和左值引用的想法正是为了确保你得到一个或另一个。不是任何一个。

通常使用它是因为如果你得到一个右值,你可以移动它。如果你得到一个左值,你需要复制它。您还可以确保函数只能使用右值或左值调用。

传递函数时,通常的方法是按值获取参数。这也适用于右值和左值。这就是它(几乎总是?)在标准库中完成的方式。函数指针和 Functor 通常复制起来非常便宜。

如果你想要一个可以接受右值和左值的签名,你可以使用 const & .

另请注意,由于 Function&&是一个模板参数,它是一个转发引用。这意味着它将成为右值引用或左值引用,具体取决于您传入的内容。

当您使用显式指定的参数调用函数时

addv<Adder,int,int>(adder,a,b);
       ^--this

模板参数将准确推导出为 Adder ,然后您的函数将只接受右值,因为签名说 Function&& -> Adder&& .

使代码工作的简单方法是不显式指定模板参数。
addv(adder,a,b);

然后您可以删除原型(prototype) 2,所有函数调用都将起作用。

如果你真的想或者需要指定参数,可以使用std::move在调用站点将左值转换为右值。
addv<Adder,int,int>(std::move(adder),a,b);

编辑:转换可能有点误导。其实是类型转换。除了值类别外,没有任何变化。

关于c++ - 可变参数模板 : lvalue vs rvalue references,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60936652/

相关文章:

c++ - 在没有 const_iterator 的情况下使用 boost_foreach

c++ - 相当于 `find_if` 的二进制搜索

c++ - 如何打印 C++ 中相互依赖的枚举值的名称

c++ - 无法解释的不同 omp_get_wtime() 用于相同的精确计算

c++ - 使用多线程处理队列消息

c++ - 如何使用 Direct3D9 缩放 IDirect3DTexture9?

c++ - Qt GUI 应用程序中的控制台输出?

c++ 函数——哪个日期是第一个/最后一个?

c++ - 如何测试共享对象/共享库是否已正确编译?

c++ - 函数参数 : Copy or pointer?