复制初始化的奇怪行为,不调用复制构造函数!

我正在阅读直接初始化和复制初始化之间的区别(第 8.5/12 节):

I was reading the difference between direct-initialization and copy-initialization (§8.5/12):

T x(a);  //direct-initialization
T y = a; //copy-initialization

我从阅读 copy-initialization 是它需要 可及性非显式复制构造函数,否则程序将无法编译.我通过编写以下代码验证了它:

What I understand from reading about copy-initialization is that it needs accessible & non-explicit copy-constructor, or else the program wouldn't compile. I verified it by writing the following code:

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) 说:

GCC gives an error (ideone) saying:

prog.cpp:8: 错误:'A::A(const A&)' 是私有的

prog.cpp:8: error: ‘A::A(const A&)’ is private

到目前为止一切都很好,重申 Herb Sutter 所说的,

So far everything is fine, reaffirming what Herb Sutter says,

拷贝初始化是指使用拷贝构造函数对对象进行初始化,必要时先调用用户自定义的转换,等价于T t = u;"的形式:

Copy initialization means the object is initialized using the copy constructor, after first calling a user-defined conversion if necessary, and is equivalent to the form "T t = u;":

<小时>

之后,我通过注释 private 关键字使 copy-ctor 可访问.现在,我自然希望打印以下内容:


After that I made the copy-ctor accessible by commenting the private keyword. Now, naturally I would expect the following to get printed:

A(const A&)

A(const A&)

但令我惊讶的是,它打印的是这个(ideone):

But to my surprise, it prints this instead (ideone):

A(int i)

为什么?

好的,我知道首先使用 10 创建一个 A 类型的临时对象,它是 int 类型,使用 A(int i),在这里根据需要应用转换规则(第 8.5/14 节),然后它应该调用 copy-ctor 来初始化 a.但它没有.为什么?

Alright, I understand that first a temporary object of type A is created out of 10 which is int type, by using A(int i), applying the conversion rule as its needed here (§8.5/14), and then it was supposed to call copy-ctor to initialize a. But it didn't. Why?

如果允许实现消除调用复制构造函数的需要(第 8.5/14 节),那么当复制构造函数声明为 private 时,为什么它不接受代码?毕竟,它没有调用它.这就像一个被宠坏的孩子,第一次生气地要一个特定的玩具,当你给他一个,特定的玩具,他就把它扔在你背后.:|

If an implementation is permitted to eliminate the need to call copy-constructor (§8.5/14), then why is it not accepting the code when the copy-constructor is declared private? After all, its not calling it. Its like a spoiled kid who first irritatingly asks for a specific toy, and when you give him one, the specific one, he throws it away, behind your back. :|

这种行为会不会很危险?我的意思是,我可能会在 copy-ctor 中做一些其他有用的事情,但如果它不调用它,那么它不会改变程序的行为吗?

Could this behavior be dangerous? I mean, I might do some other useful thing in the copy-ctor, but if it doesn't call it, then does it not alter the behavior of the program?

推荐答案

你问编译器为什么要做访问检查?C++03 中的 12.8/14:

Are you asking why the compiler does the access check? 12.8/14 in C++03:

一个程序是非良构的,如果副本构造函数或复制赋值对象的运算符是隐式的使用和特殊的成员函数无法访问

A program is ill-formed if the copy constructor or the copy assignment operator for an object is implicitly used and the special member function is not accessible

当实现省略复制构造"(12.8/15 允许)时,我不认为这意味着复制 ctor 不再隐式使用",它只是没有被执行.

When the implementation "omits the copy construction" (permitted by 12.8/15), I don't believe this means that the copy ctor is no longer "implicitly used", it just isn't executed.

或者您是在问为什么标准这么说?如果复制省略是此规则关于访问检查的一个例外,那么您的程序在成功执行省略的实现中将是格式良好的,但在不执行此操作的实现中格式是错误的.

Or are you asking why the standard says that? If copy elision were an exception to this rule about the access check, your program would be well-formed in implementations that successfully perform the elision, but ill-formed in implementations that don't.

我很确定作者会认为这是一件坏事.当然,以这种方式编写可移植代码更容易――编译器会告诉您,如果您编写的代码尝试复制不可复制的对象,即使该副本恰好在您的实现中被省略.我怀疑它也可能不方便实施者确定优化是否会成功在检查访问之前(或将访问检查推迟到尝试优化之后),尽管我不知道这是否有保证考虑.

I'm pretty sure the authors would consider this a Bad Thing. Certainly it's easier to write portable code this way -- the compiler tells you if you write code that attempts to copy a non-copyable object, even if the copy happens to be elided in your implementation. I suspect that it could also inconvenience implementers to figure out whether the optimization will be successful before checking access (or to defer the access check until after the optimization is attempted), although I have no idea whether that warranted consideration.

这种行为会不会很危险?一世意思是,我可能会做一些其他有用的事情复制ctor中的东西,但如果它不调用它,那么它不是改变程序的行为?

Could this behavior be dangerous? I mean, I might do some other useful thing in the copy-ctor, but if it doesn't call it, then does it not alter the behavior of the program?

当然这可能很危险 - 当且仅当对象被实际复制时,复制构造函数中的副作用才会发生,并且您应该相应地设计它们:标准说可以省略副本,所以不要将代码放在复制构造函数,除非您乐于在 12.8/15 中定义的条件下省略它:

Of course it could be dangerous - side-effects in copy constructors occur if and only if the object is actually copied, and you should design them accordingly: the standard says copies can be elided, so don't put code in a copy constructor unless you're happy for it to be elided under the conditions defined in 12.8/15:

MyObject(const MyObject &other) {
    std::cout << "copy " << (void*)(&other) << " to " << (void*)this << "
"; // OK
    std::cout << "object returned from function
"; // dangerous: if the copy is
      // elided then an object will be returned but you won't see the message.
}

相关文章