【问题标题】:Passing temporary objects as argument (C++)将临时对象作为参数传递 (C++)
【发布时间】:2014-09-29 17:10:31
【问题描述】:

临时对象有时是用 C++ 创建的。 STL valarray 类的一个示例是 slice_array,每次选择 valarray 的一系列索引时都会调用它。 http://www.cplusplus.com/reference/valarray/slice_array/

我的问题是这样的:

当将这些临时对象作为参数传递给函数时,是传递这些对象的副本,还是仅传递一个引用?

例如想象一下这两个简单的函数:

double simple_product(double* inp,int length){
  double res=1;
  for(int i=0;i<length;++i){
      res = res*inp[i];
  }
  return(res);
}

double sum_selected(valarray<double> v){

    simple_product(&v[0],v.size());
    return(v.sum());
}

如果我按以下方式称呼他们:

valarray<double> valery(10,10);
size_t sel[] = {1,3,4};
valarray<size_t> selection (sel,3);
cout << sum_selected(valery[selection]);

是否会在sum_selected函数的堆栈中临时创建一个大小为3*size_t的新对象?

请注意将函数声明为:double sum_selected(valarray&lt;double&gt; &amp; v) 不允许(临时对象只能绑定到 const 引用)。

这很有趣的原因是,例如这里,不可能将函数声明为:

double sum_selected(const valarray&lt;double&gt; &amp; v),因为这样就无法调用函数 simple_product(假定它是不可更改的)。但是,在大数组的情况下,制作传递参数的临时副本会影响内存。

【问题讨论】:

  • 编译您的代码并启用优化。此外,生成程序集列表并查看发生了什么。
  • 你为什么不把你的参数改成const double * inp,然后你把另一个参数改成const valarray&lt;double&gt;&amp; v
  • simple_product 真的一成不变吗?因为,在我看来,这是错误的。如果无法更改,当您将 const valarray 传递给函数时,这将是可接受的 const_cast 用法,它不会修改它,但由于某种原因不会将其视为 const。
  • @NeilKirk:这将是我希望工作的创可贴,但如果传递一个真正恒定的对象,那将是 UB。
  • @Deduplicator 它不是 UB,因为函数 simple_product 不会修改它。这是一个创可贴,但他说功能不能改变。

标签: c++ function arguments pass-by-reference rvalue-reference


【解决方案1】:

如果函数声明为按值获取其参数,则按值传递,创建对象的新副本:

void f(thing t);           // pass by value

如果它被声明为通过引用获取它的参数,那么它是通过引用传递的。但它只能通过constrvalue 临时引用:

void f(thing const & t);   // OK: const lvalue reference
void f(thing && t);        // OK: rvalue reference
void f(thing & t);         // Error: lvalue reference can't bind to temporary

通过引用传递不会创建新对象,并且临时对象的生命周期在函数调用期间有效,直到创建它的语句结束。

【讨论】:

  • 按值传递的参数并不总是被复制。
  • @rightfold:确实,整个故事要复杂得多,从临时复制来初始化相同类型的参数可能会被省略。我将保持简单的答案,并将其留给任何想了解复制语义的血腥细节的人:stackoverflow.com/questions/12953127
  • 更糟糕的是,上面的案例有一个thing,它的类型与传递给它的参数不同。 thing 不会在 OP 的问题中复制,无论省略如何,而是构造。
  • @Mike 谢谢,我不知道右值引用的存在!
【解决方案2】:

valarray&lt;T&gt;::operator[](valarray&lt;size_t&gt; const&amp;) 返回一个indirect_array&lt;T&gt; (reference),或valarray&lt;T&gt; 用于const 限定运算符,而不是slice_array

如果您希望能够以连续数组的形式访问所选元素,则需要将它们收集到一个连续数组中。转换构造函数valarray&lt;T&gt;::valarray(indirect_array&lt;T&gt; const&amp;) 会为您执行此操作。在这种情况下,引用语义将毫无用处,因为没有现有对象可以将所需的元素连续排列。

将函数签名更改为double sum_selected(valarray&lt;double&gt; const&amp;) 对您的代码没有任何影响,因为无论如何都会构造一个临时的valarray&lt;double&gt;。如果直接传递valery,不加下标,效率会更高。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-07
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    相关资源
    最近更新 更多