将多个右值和左值传递给一个函数而不创建重载

passing multiple rvalues and lvalues to a function without creating overloads

本文关键字:一个 函数 重载 创建 值传      更新时间:2023-10-16

假设您正在为客户端库提供一个具有多个引用参数的函数。

为了简单起见,我们假设我们有3个参数,这些参数是对int的各种引用(因此您可以假设这些常数也将由initializer_list创建(。

从调用站点,应该可以将临时创建的常量(rvvalue(传递给函数(在这种情况下可以考虑测试代码(,以及传递给另一个实体拥有的对象的实际引用。

到目前为止,我已经提出了以下解决方案:

void bar(int &x, int &y, int &z) { }
// use a template wrapper to turn rvalues into lvalues.
template <typename T, typename U, typename V>
void foo(T &&t, U &&u, V &&v) { 
    bar(t,u,v);
}
// create a humongous amount of overloads
void baz(int &&x, int &y, int &z) { }
void baz(int &x, int &&y, int &z) { }
void baz(int &&x, int &&y, int &z) { }
void baz(int &x, int &y, int &&z) { }
void baz(int &&x, int &y, int &&z) { }
void baz(int &x, int &&y, int &&z) { }
void baz(int &&x, int &&y, int &&z) { }
// claim ownership of the objects and create temporaries
void bam(int x, int y, int z) { }
int main() {
    int i = 1;
    foo(i,2,3);
    foo(2,i,3);
    foo(2,3,i);
    bar(i,2,3); // doesn't compile
    bar(2,i,3);
    bar(2,3,i);
    baz(i,2,3); // requires 8 overloads
    baz(2,i,3); 
    baz(2,3,i);
    return 0;
}   

我对所有的解决方案都不完全满意,因为每一个都有缺点。有没有更清洁的替代方案来解决这个问题?

这个问题其实并不简单,但有一些指导方针经过多年的发展,实际上在C++11中没有太大变化。对于以下内容,我们将假设您有一个无副作用、独立的函数(构造函数和某些成员函数的指导方针略有不同(。

我对独立功能的建议是:

  1. 按值传递基元数据类型(int、double、bool等(
  2. 通过const引用传递复杂的数据类型(例如const std::vector&(,除非您需要函数中的副本,然后通过值传递
  3. 如果您的函数是模板化的,请使用const引用(例如const T&(,因为左值和右值都将绑定到const引用
  4. 如果你的函数是模板化的,并且你打算完善转发,那么就使用转发引用(例如T&&,也称为通用引用(

因此,对于使用整数的示例,我将使用bam函数:

void bam(int x, int y, int z) { }

Herb Sutter去年在CppCon上发表了一篇有趣的演讲其中包括函数的输入参数:https://www.youtube.com/watch?v=xnqTKD8uD64

关于stackoverflow的另一个有趣的帖子与这个问题有关:正确使用右值参考作为参数

构造函数更新:

构造函数与上述建议的主要区别在于更加强调复制输入并使对象归类所有。二传手也是如此。主要原因是更安全的资源管理(避免数据竞争或访问无效内存(。

这是一个完美的转发问题。避免baz的8个重载的方法是使用所谓的通用引用:

template <typename T, typename U, typename V>
void baz(T &&t, U &&u, V &&v) { }

在上文中,tuv忠实地复制了参数的r/l值。例如,假设您最初的预期功能是:

void baz(vector<int> &t, vector<int> &u, vector<int> &v) { }

以及其他7个过载。在模板化版本中,如果调用baz(a,b,c),其中ab是右值,而c不是,则TU将被推导为vector<int>&&,而V将被推断为vector<int>&。因此,您就拥有了此实现中所需的所有信息。

template<class T>
struct lrvalue {
  T&t;
  lrvalue(T&in):t(in){}
  lrvalue(T&&in):t(in){}
};

取CCD_ 17。问题解决了吗?你可以把它们打扮一下,也许可以继承reference_wrapper或类似的东西,而不是T&t

您也可以在调用时进行投射:

template<class T>
T& lvalue(T&&t){return t;}
template<class T>
T& lvalue(T&t)=delete;//optional

现在,如果您想将foo{}传递给foo&,您可以。CCD_ 22。