我正在阅读直接初始化和复制初始化(§8.5/ 12)之间的区别:
T x(a); //direct-initialization
T y = a; //copy-initialization
我从阅读有关copy-initialization的内容中了解到,它需要accessible & non-explicit copy-constructor,否则程序将无法编译。我通过编写以下代码验证了它:
struct A
{
int i;
A(int i) : i(i) { std::cout << " A(int i)" << std::endl; }
private:
A(const A &a) { std::cout << " A(const A &)" << std::endl; }
};
int main() {
A a = 10; //error - copy-ctor is private!
}
GCC提出错误(ideone)说:
prog.cpp:8:错误:'A :: A(const A&amp;)'是私有的
到目前为止一切正常,reaffirming what Herb Sutter says,
复制初始化意味着在必要时首次调用用户定义的转换后,使用复制构造函数初始化对象,并且等效于“T t = u;”形式:
之后,我通过评论private
关键字来访问copy-ctor。现在,我自然希望以下内容得到印刷:
A(const A&amp;)
但令我惊讶的是,它打印了这个(ideone):
A(int i)
为什么?
好的,我了解首先使用A
10
创建int
类型的临时对象,使用A(int i)
,将转换规则应用为它需要在这里(§8.5/ 14),然后它应该调用copy-ctor来初始化a
。但事实并非如此。为什么?
如果允许实现消除调用copy-constructor(第8.5 / 14节)的需要,那么在复制构造函数被声明为private
时为什么不接受代码?毕竟,它没有称之为。它就像一个被宠坏的孩子,他第一次恼怒地要求一个特定的玩具,当你给他一个特定的一个时,他把它丢在背后。 :|
这种行为会有危险吗?我的意思是,我可能会在copy-ctor中做一些其他有用的事情,但是如果它没有调用它,那么它是否会改变程序的行为?
答案 0 :(得分:10)
您是在问为什么编译器会进行访问检查? 12.8 / 14 in C ++ 03:
如果副本,程序格式不正确 构造函数或复制赋值 对象的运算符是隐式的 使用和特殊成员功能 无法访问
当实现“省略复制结构”(由12.8 / 15允许)时,我不认为这意味着复制文件不再“隐式使用”,它只是不执行。
或者你在问为什么标准会这么说?如果复制省略是关于访问检查的此规则的一个例外,那么您的程序将在成功执行省略的实现中形成良好的形式,但在不实现的实现中格式不正确。
我很确定作者会认为这是一件坏事。当然,以这种方式编写可移植代码更容易 - 编译器会告诉您是否编写了尝试复制不可复制对象的代码,即使在您的实现中省略了副本。我怀疑实施者在检查访问之前判断优化是否会成功,或者在尝试优化之后推迟访问检查,这也可能会给实施者带来不便,尽管我不知道是否保证考虑。
这种行为会有危险吗?一世 意思是,我可能会做一些有用的事情 副本中的东西,但如果它 不称之为,然后不这样做 改变程序的行为?
当然它可能很危险 - 当且仅当对象被实际复制时才会出现副本构造函数中的副作用,并且应该相应地设计它们:标准说副本可以省略,所以不要将代码放入复制构造函数,除非你很高兴在12.8 / 15中定义的条件下被删除:
MyObject(const MyObject &other) {
std::cout << "copy " << (void*)(&other) << " to " << (void*)this << "\n"; // OK
std::cout << "object returned from function\n"; // dangerous: if the copy is
// elided then an object will be returned but you won't see the message.
}
答案 1 :(得分:5)
C ++显式允许几个涉及实际更改程序语义的复制构造函数的优化。 (这与大多数优化形成对比,这些优化不会影响程序的语义)。特别是,有几种情况允许编译器重新使用现有对象,而不是复制一个,如果它知道现有对象将无法访问。这种(复制结构)就是这样一种情况;另一个类似的情况是“返回值优化”(RVO),如果你声明保存函数返回值的变量,那么C ++可以选择在调用者的帧上分配它,这样它就不需要了在函数完成时将其复制回调用者。
一般情况下,在C ++中,如果您定义了一个副作用或除了复制之外的其他任何操作,那么您正在玩火。
答案 2 :(得分:4)
在任何编译器中,语法[和语义]分析过程都是在代码优化过程之前完成的。
代码必须在语法上有效,否则甚至不会编译。它仅在后期阶段(即代码优化)中,编译器决定忽略它创建的临时阶段。
所以你需要一个可访问的副本c-tor。
答案 3 :(得分:1)
Here你可以找到这个(带有你的评论;)):
[标准]也说临时副本 可以省略,但语义 约束(例如,可访问性) 复制构造函数仍然必须 检查。
答案 4 :(得分:0)
RVO和NRVO,哥们。复制椭圆的完美案例。
答案 5 :(得分:0)
这是编译器的优化。
评估时:A a = 10;
代替:
首先通过A(int)
;
通过复制构造函数构建a
并传入临时文件;
编译器只需使用a
构建A(int)
。