我正在阅读直接初始化和复制初始化之间的区别(第8.5/12节):
T x(a); //direct-initialization
T y = a; //copy-initialization我从阅读复制初始化的文章中了解到,它需要可访问和非显式复制构造函数,否则程序就不会编译。我通过编写以下代码对其进行了验证:
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说了一个错误(意为):
prog.cpp:8:错误:‘a::a(const&)’是私有的
到目前为止一切都很好,重申赫伯·萨特的话
复制初始化意味着对象在第一次调用用户定义的转换(如果需要的话)之后,使用复制构造函数()初始化,并且相当于表单"T = u;":
之后,我通过注释private关键字使复制-ctor可访问。现在,我很自然地希望以下内容能够被打印出来:
A(const A&)
但令我惊讶的是,它却打印了这个(意为):
A(int i)
为什么?
好的,我知道首先A类型的临时对象是从10 ( int类型)中创建的,通过使用A(int i),在这里应用转换规则(§8.5/14),然后调用复制ctor来初始化a。但事实并非如此。为什么?
如果允许实现不需要调用复制构造函数(§8.5/14),那么为什么当复制构造函数声明为private时它不接受代码?毕竟,这并不叫它。这就像一个被宠坏的孩子第一次恼怒地要一个特定的玩具,当你给他一个特定的玩具时,他会把它扔在你背后。:
这种行为会不会很危险?我的意思是,我可能会在复制器中做一些其他有用的事情,但是如果它不调用它,那么它不会改变程序的行为吗?
发布于 2011-05-28 17:51:14
您是在问编译器为什么要检查访问吗?在C++03中是12.8/14:
如果隐式使用对象的复制构造函数或复制赋值运算符,并且无法访问特殊的成员函数,则程序的格式不正确。
当实现“省略复制构造”(12.8/15允许的)时,我不认为这意味着复制ctor不再被“隐式使用”,它只是没有被执行。
或者你是在问为什么标准会这么说?如果复制省略是关于访问检查的规则的一个例外,那么您的程序将在成功执行省略的实现中形成良好的格式,而在不成功执行的实现中则会形成错误的格式。
我很肯定作者会认为这是件坏事。当然,用这种方式编写可移植代码更容易--编译器告诉您,如果您编写的代码试图复制不可复制的对象,即使副本碰巧在您的实现中被省略了。我怀疑,在检查访问之前确定优化是否成功(或者将访问检查推迟到尝试优化之后进行)也会给实现者带来不便,尽管我不知道这是否值得考虑。
这种行为会不会很危险?我的意思是,我可能会在复制器中做一些其他有用的事情,但是如果它不调用它,那么它不会改变程序的行为吗?
当然,只有当对象实际被复制时,复制构造函数才会产生危险的副作用,并且您应该相应地设计它们:标准说副本可以被省略,所以不要将代码放在副本构造函数中,除非您乐于在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.
}发布于 2011-05-28 18:02:05
C++显式地允许涉及实际更改程序语义的复制构造函数的几个优化。(这与大多数不影响程序语义的优化形成了对比)。特别是,有几种情况下,编译器可以重用一个现有的对象,而不是复制一个对象,如果它知道现有的对象将变得不可访问。这种情况(复制构造)就是这样一种情况;另一种类似的情况是“返回值优化”(RVO),如果您声明保存函数返回值的变量,那么C++可以选择在调用方的帧上分配它,这样它就不需要在函数完成时将其复制回调用方。
通常,在C++中,如果您定义了一个具有副作用的复制构造函数,或者只执行复制以外的任何操作,那么您就是在玩fire。
发布于 2011-05-28 17:20:59
在任何编译器中,语法和语义分析过程都是在代码优化过程之前完成的。
代码必须在语法上是有效的,否则它甚至不会编译。只有在后期阶段(即代码优化),编译器才决定删除它创建的临时代码。
所以你需要一份可接近的副本。
https://stackoverflow.com/questions/6163040
复制相似问题