c ++避免复制通过引用函数传递的参数

时间:2017-02-10 16:05:15

标签: c++ copy pass-by-reference eigen

假设我通过const引用将变量传递给函数,比如func2。经过一些测试,我想在调用它之前修改它的参数。这是一个解释我的目标的伪代码。

struct strA
{ 
    int a;
    VectorXd v;
};

strA func2(const int& a, const VectorXd& v){...};
strA func1(...){...};

int main(){

    int a;
    VectorXd v;
    bool test;

    if(test){ 
       strA B;
       // some amount of work is required to get the members of B
       B = func1(...);
       func2(B.a, B.v); 

    } else {
       func2(a, v);
    };

    return 0;
}

这段代码工作正常但是为了可读性和通用性,我想知道是否存在更好的解决方案。这是另一种选择

strA func3(const int& a, const VectorXd& v, test)
{ 
    strA B;
    if(test){ // some amount of work is required to get the members of B
        B = func1(...);

    } else { // map the arguments to a structure
        B.a = a; // the original arguments passed by constant reference
        B.v = v; 
    };

    return B;
}; 

int main(){

    int a;  
    VectorXd v;
    bool test;
    strA B;
    B = func3(a,v,test);
    func2(B.a, B.v);

    return 0; 
}

test==true(显然)没有问题保持结构B和参数a和v作为那些实体的情况下。但是,当test==false替代只是在结构B中复制原始参数a和v(在我的应用程序中是几个大向量)。是否有更清洁的方法或我应该坚持第一个?顺便说一下,由于这段代码出现在循环中,我很烦恼,所以我更喜欢避免在几个地方复制相同的代码。

谢谢!

2 个答案:

答案 0 :(得分:0)

我建议采用两种方法:

首先:让我们在strA内的堆上创建类func3的对象,并返回指向该对象的指针(例如strA* func3(..)而不是copy { {1}}):

strA func3(..)

然后以下列方式使用:

strA* func3(..) {
    strA* B = new strA;
    ...   // change B.someMember to B->someMember everywhere
    return B;
}

警告:您必须自己管理对象的生命周期,在使用后显式释放内存(调用strA* B; ... B = func3(..); ... delete B; )。但是,请查看std::unique_ptr和其他一些智能指针(如果您可以使用delete)。

第二:将对象的引用传递给函数,在那里修改并返回c++11

void

并使用:

void func3(/*previous arguments*/, strA* pobj) {
          // nothing to allocate
    ...   // change B.someMember to pobj->someMember everywhere
          // nothing to return as we've modified an object
}

注意:但是您的strA B; ... func3(..., &B); 仍会返回副本,因此您可以将其修改为原样或保持原样。

答案 1 :(得分:0)

您提供的两个选项具有相同的副本数量。 阅读完您的评论后,我就可以收集到这些内容:

区分Initialization和Copy构造函数/ operator =

让我们看一下这句话:

strA B;

该行初始化名为strA的{​​{1}}类型的本地变量。

现在让我们来看看这句话:

B

现在,此行从strA B = foo(3, 4); 返回的临时对象的副本初始化名为strA的{​​{1}}类型的本地变量。

  

编译器只对名为Copy Ellision and Return Value Optimization

的编译器进行了特殊优化

所以,第二行可能不太理想,但......不是真的。在第一行中,对象未使用正确的数据进行初始化,它使用默认构造函数。所以你在解决方案中做的事情如下:

B
  1. 在此解决方案中,就像第一行一样,使用默认的ctor初始化对象;
  2. foo
  3. 返回临时对象
  4. 使用默认的strA B; B = foo(3,4);
  5. 将对象复制到foo
      

    此说明根据优化情况再次变化。此外,如果您使用的是c ++ 11,由于B

    ,它会变得更复杂

    这两个世界都受到影响,不仅可能调用operator=,它还使用默认构造函数。但是代码的可读性较差,使编译器难以优化。

    我不确定您要阻止哪个副本。但我的建议是这个 -

    在测量之前不要尝试优化代码

    您可能认为您的代码会很慢,但您无法真正了解这一点。编译器几乎可以实现"黑暗魔法"尝试将代码优化到最高级别(确保在gcc上运行Move Semantics标志)

    部分代码

    由于我还不确定你要修复的是什么,我只是给你一些可能(也可能没有)帮助的指针。但首先是代码:

    operator=
    • 始终初始化并避免-O3。如果我告诉您始终使用void calc_and_call(const int& a, const VectorXd& v) { const strA B = func1(a, b); func2(b.A, b.B); } void func2_wrapper(const int& a, const VectorXd& v, test) { if(test) calc_and_call(a, v); else func2(a, v); };

    • ,这可以进一步概括
    • 保持对象的范围尽可能小(这里是两行)