1

由于复制省略,通常首选按值传递对象,只要保留内部副本即可。以下情况怎么办:

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?它依赖于内联吗?

4

1 回答 1

2

在您的示例中,我认为这没有太大区别。的构造函数A没有副作用,因此标准的复制省略规则无关紧要。重要的是优化器可以实际实现的目标。一旦构造函数被内联,我希望在这两种情况下都差不多,并且没有什么特别的原因不能内联这个构造函数(它很小并且在类定义中定义)。

如果B构造函数不能内联或A构造函数有副作用,则必须复制到数据成员(对于 和 中的基类子对象C也是如此D)。标准的复制省略规则不允许从参数到数据成员的复制被省略,因此实际上您会看到不需要的副本。

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

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

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

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

于 2012-11-08T10:33:07.810 回答