在链接构造函数的调用中复制elision

时间:2012-11-08 10:19:30

标签: c++ constructor copy-elision

由于复制省略,通常首选pass objects by value,只要保留内部副本即可。以下情况如何:

struct A
{
    A(int x, int y) : x(x), y(y) {}
    int x, y;
};

struct B
{
    B(A a) : a(a) {}                // 1
    B(const A& a) : a(a) {}         // 2
    A a;
};

struct C : A
{
    C(A a) : A(a) {}                // 3
    C(const A& a) : A(a) {}         // 4
};

struct D : B, C
{
    D(A a) : B(a), C(a) {}          // 5
    D(const A& a) : B(a), C(a) {}   // 6
};

链接副本是否仍然被删除,即1,3和5更好?或者更确切地说是2,4,6?它取决于内联吗?

1 个答案:

答案 0 :(得分:2)

在你的例子中,我认为它没有多大区别。 A的构造函数没有副作用,因此标准关于复制省略的规则是无关紧要的。重要的是优化者可以实现的目标。一旦构造函数被内联,我希望在两种情况下大致相同,并且没有特别的理由说明为什么这个构造函数不能被内联(它很小并且它在类定义中定义)。

如果B构造函数无法内联或A构造函数有副作用,则必须复制到数据成员(C中的基类子对象相同)和D)。关于复制省略的标准规则不允许从参数到数据成员的复制被删除,因此在现实情况下,您将看到不需要的副本。

假设类型A具有比复制更有效的移动构造函数,在调用者传递临时对象的情况下,以下是明显的胜利:

struct B
{
    B(A a) : a(std::move(a)) {}
    A a;
};

原因是这可以直接将临时构造到函数参数中,然后将其移动到数据成员中。 2/4/6不能(安全地)从左值引用到const移动到数据成员中。 1/3/5可以安全地移动,因为参数a不再使用,但是不允许编译器为自己进行更改,除非(a)你赋予它std::move的权限,或(b)它们在“as-if”规则下是等同的。

在调用者传递左值的情况下,我的构造函数可能比(2)略慢,因为我的构造函数复制然后移动,而(2)只是复制。您可以忽略此成本(因为移动应该非常便宜),或者您可以编写单独的const A&A&&构造函数。我希望一般的经验法则是做前者。