2

我的 C++ 书中的以下引用:

当我们使用直接初始化时,我们要求编译器使用普通函数匹配来选择与我们提供的参数最匹配的构造函数。当我们使用复制初始化时,我们要求编译器将右手操作数复制到正在创建的对象中,并在必要时转换该操作数。

对我来说,这个粗体字会产生一些歧义。例如,这听起来像是将右侧的操作数转换为类类型,然后使用了复制构造函数;

string s = "hello";

会成为...

string s = string("hello"); 

它使用复制构造函数。如果这是真的,那么我的测试程序;

#include <iostream>
using namespace std;

class A{
public:
    A(const A& b): data(b.data) { cout << "The first way" << endl;}
    A(const char* c): data(c) { cout << "The second way" << endl;}
    string data;

};
int main(){
    A first("hello");
    A second = "sup";
}

应该产生“第二种方式,第二种方式,第一种方式”。然而,它改为打印“第二种方式,第二种方式”。由此我会得出结论,它使用的是 const char* 构造函数而不是复制构造函数。我会很好的,除非后来它说...

在复制初始化期间,允许(但不是必须)编译器跳过复制/移动构造函数并直接创建对象。即允许编译器重写

string null_book = "9-999-99999-9"; 

进入

string null_book("9-999-99999-9");

然而,即使编译器省略了对复制/移动构造函数的调用,复制/移动构造函数也必须存在并且必须在程序中的那个点是可访问的(例如,不是私有的)。

我不确定为什么复制构造函数甚至需要在这些示例中提及,不是

 string null_book = "9-999-99999-9"

总是隐含地意味着无论如何都在使用 const char* 构造函数?实际上,需要定义复制构造函数才能使上述内容起作用,这对我来说意义不大。但是,如果我将“const A&”构造函数设为私有(其余为公共),那么我的程序将无法运行。为什么必须为甚至不涉及它的隐式转换定义复制构造函数?"string null_book = "9-999-99999-9"" 使用什么构造函数?

4

1 回答 1

6

string null_book = "9-999-99999-9";意味着string null_book = string("9-999-99999-9");

这使用const char *构造函数构造一个临时对象,然后从临时对象null_book复制/移动构造,然后将临时对象销毁。

复制/移动构造意味着如果可用则使用移动构造函数;否则使用复制构造函数)。

但是,这种情况也适用于复制省略。您实际上在问题中引用了复制省略规范,所以我不会重复它。

编译器可以选择为临时对象和临时对象使用相同的内存空间null_book,并省略对临时对象析构函数和null_book复制/移动构造函数的调用。

在您的情况下,编译器确实选择这样做,这就是为什么您看不到任何复制构造函数输出的原因。

一些编译器允许通过开关禁用复制省略,例如 gcc/clang -fno-elide-constructors

有关复制省略的更多信息

于 2015-08-25T23:28:37.890 回答