假设我有这样一个函数:

void my_test()
{
    A a1 = A_factory_func();
    A a2(A_factory_func());

    double b1 = 0.5;
    double b2(0.5);

    A c1;
    A c2 = A();
    A c3(A());
}

在每一组中,这些陈述是否相同?或者在某些初始化中是否存在额外的(可能是可优化的)副本?

我见过有人两种说法都说。请引用原文作为证明。也请添加其他案例。


当前回答

这是来自Bjarne Stroustrup的c++编程语言:

带有=的初始化被认为是复制初始化。原则上,初始化器(我们要从中复制的对象)的副本被放置到初始化的对象中。但是,这样的副本可以被优化掉(省略),如果初始化式是右值,则可以使用move操作(基于move语义)。省略=可以显式地初始化。显式初始化称为直接初始化。

其他回答

就这部分回答:

A c2 = A();c3 (());

由于大多数答案都是c++11之前的,我补充了c++11必须说的:

A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). If the type specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.

所以不管优化与否,它们在标准上是等价的。 请注意,这与前面提到的其他答案是一致的。只是为了正确起见,引用了标准的内容。

赋值不同于初始化。

以下两行代码都进行初始化。一个构造函数调用完成:

A a1 = A_factory_func();  // calls copy constructor
A a1(A_factory_func());   // calls copy constructor

但它不等于:

A a1;                     // calls default constructor
a1 = A_factory_func();    // (assignment) calls operator =

我现在还没有一篇文章来证明这一点,但这很容易实验:

#include <iostream>
using namespace std;

class A {
public:
    A() { 
        cout << "default constructor" << endl;
    }

    A(const A& x) { 
        cout << "copy constructor" << endl;
    }

    const A& operator = (const A& x) {
        cout << "operator =" << endl;
        return *this;
    }
};

int main() {
    A a;       // default constructor
    A b(a);    // copy constructor
    A c = a;   // copy constructor
    c = b;     // operator =
    return 0;
}

值得注意的是:

[12.2/1]类类型的临时对象可以在不同的上下文中创建:…在一些初始化中(8.5)。

例如,用于复制初始化。

[12.8/15]当满足某些条件时,允许实现省略类对象的复制构造…

换句话说,一个好的编译器不会在可以避免的情况下创建副本进行复制初始化;相反,它将直接调用构造函数——即,就像直接初始化一样。

换句话说,复制初始化就像在大多数<opinion>的情况下直接初始化,其中已经编写了可理解的代码。由于直接初始化可能会导致任意(因此可能是未知的)转换,我更喜欢在可能的情况下总是使用复制初始化。(额外的是,它实际上看起来像初始化。)</opinion>

技术血淋淋的景象: [12.2/1接续以上]即使避免了临时对象的创建(12.8),也必须遵守所有的语义限制,就像创建了临时对象一样。

很高兴我不是在写c++编译器。

First grouping: it depends on what A_factory_func returns. The first line is an example of copy initialization, the second line is direct initialization. If A_factory_func returns an A object then they are equivalent, they both call the copy constructor for A, otherwise the first version creates an rvalue of type A from an available conversion operators for the return type of A_factory_func or appropriate A constructors, and then calls the copy constructor to construct a1 from this temporary. The second version attempts to find a suitable constructor that takes whatever A_factory_func returns, or that takes something that the return value can be implicitly converted to.

第二组:逻辑完全相同,只是内建类型没有任何奇异构造函数,因此它们实际上是相同的。

Third grouping: c1 is default initialized, c2 is copy-initialized from a value initialized temporary. Any members of c1 that have pod-type (or members of members, etc., etc.) may not be initialized if the user supplied default constructors (if any) do not explicitly initialize them. For c2, it depends on whether there is a user supplied copy constructor and whether that appropriately initializes those members, but the members of the temporary will all be initialized (zero-initialized if not otherwise explicitly initialized). As litb spotted, c3 is a trap. It's actually a function declaration.

当初始化一个对象时,你可以看到显式和隐式构造函数类型的区别:

类:

class A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
};

class B
{
    explicit B(int) { }
    explicit B(int, int) { }
};

在main函数中:

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
}

默认情况下,构造函数是隐式的,所以你有两种方法来初始化它:

A a1 = 1;        // this is copy initialization
A a2(2);         // this is direct initialization

通过将结构定义为显式,你只有一种方式是直接的:

B b2(2);        // this is direct initialization
B b5 = (B)1;    // not problem if you either use of assign to initialize and cast it as static_cast