将多个rvalues和lvalues传递给函数而不创建重载

时间:2015-06-03 23:40:45

标签: c++ c++11 rvalue-reference

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

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

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

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

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;
}   

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

3 个答案:

答案 0 :(得分:1)

这是完美的转发问题。避免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不是T和{{1} } U将被推断为vector<int>&&,而V将推断为vector<int>&。因此,您可以获得此实现中所需的所有信息。

答案 1 :(得分:1)

这个问题实际上并非无足轻重,但有些指导方针已经发展多年,并且实际上并没有因C ++ 11而发生太大变化。对于以下内容,我们假设您具有无副作用的独立函数(指南对于构造函数和某些成员函数略有不同)。

我建议的独立功能是:

  1. 按值
  2. 传递原始数据类型(int,double,bool等)
  3. 通过const引用传递复杂数据类型(例如const std :: vector&amp;),除非你需要在函数中复制然后传递值
  4. 如果您的函数是模板化的,请使用const引用(例如const T&amp;),因为lvalue和rvalue都将绑定到const引用
  5. 如果您的功能是模板化的,并且您打算完美转发,那么请使用转发参考(例如T&amp;&amp;,也称为通用参考)
  6. 因此,对于使用整数的示例,我将使用bam函数:

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

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

    有关此问题的另一篇关于stackoverflow的有趣帖子: Correct usage of rvalue references as parameters

    构造函数更新:

    构造函数与上述建议的主要区别在于更强调复制输入并使对象拥有该类。对于制定者来说也是如此。主要原因是更安全的资源管理(避免数据争用或访问无效内存)。

答案 2 :(得分:1)

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

lrvalue<int>。问题解决了?你可以装扮它们,也许继承自reference_wrapper或某些而不是T&t

您还可以在致电时投票:

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

现在,如果您想将foo{}传递给foo&,您可以。 lvalue(foo{})

相关问题