C++意外行为(我的临时演员在哪里!?)

Ale*_*eal 5 c++

这是一个r值实验,但当gcc向我抱怨缺少move-constructor(我已删除它)并且没有回复到复制构造函数(正如我所料)时,它发生了变异我然后删除了-std =从标志c ++ 11尝试下面看到的,它有很多输出(它最初没有),因为我试图弄清楚为什么它不起作用(我知道如何调试,但我发现stdout上的消息是发生事情的一个很好的指标)

这是我的代码:

#include <iostream>

class Object {
public:
    Object() { id=nextId; std::cout << "Creating object: "<<id<<"\n"; nextId++; }
    Object(const Object& from) {
         id=nextId; std::cout << "Creating object: "<<id<<"\n"; nextId++;
        std::cout<<"(Object: "<<id<<" created from Object: "<<from.id<<")\n";
    }
    Object& operator=(const Object& from) {
        std::cout<<"Assigning to "<<id<<" from "<<from.id<<"\n";
        return *this;
    }
    ~Object() { std::cout<<"Deconstructing object: "<<id<<"\n";}

private:
    static int nextId;
    int id;
};

int Object::nextId = 0;

Object test();

int main(int,char**) {
    Object a;
    std::cout<<"A ought to exist\n";
    Object b(test());
    std::cout<<"B ought to exist\n";
    Object c = test();
    std::cout<<"C ought to exist\n";
    return 0;
}


Object test() {
    std::cout<<"In test\n";
    Object tmp;
    std::cout<<"Test's tmp ought to exist\n";
    return tmp;
}
Run Code Online (Sandbox Code Playgroud)

输出:

Creating object: 0
A ought to exist
In test
Creating object: 1
Test's tmp ought to exist
B ought to exist
In test
Creating object: 2
Test's tmp ought to exist
C ought to exist
Deconstructing object: 2
Deconstructing object: 1
Deconstructing object: 0
Run Code Online (Sandbox Code Playgroud)

我使用解构,因为解构已经是一个词,有时我使用析构函数,我对这个词我不太满意,我赞成析构函数作为名词.

这是我的预期:

A to be constructed
tmp in test to be constructed, a temporary to be created from that 
    tmp, tmp to be destructed(?) 
that temporary to be the argument to B's copy constructor
the temporary to be destructed.
C's default constructor to be used
"" with a temporary from `test`
C's assignment operator to be used
the temporary to be destructed
c,b,a to be destructed.
Run Code Online (Sandbox Code Playgroud)

我被称为"顽固的C",我试图学习使用C++而不是"C with namespaces".

有人可能会说"编译器将其优化出来"我希望那个人现在或永远不会用这样的答案回答问题,优化不能改变程序状态,它必须好像一切都按照规范说的那样发生,所以编译器可以通过在cout上添加包含数字的消息来幽默我,它可能不会费心甚至增加数字等,但程序的输出将与它执行代码描述的所有内容相同.

所以这不是优化,发生了什么?

jro*_*rok 5

它是一种优化,是唯一允许改变程序可观察行为的优化.

这是段落12.8./31,取自标准草案n3337(强调我的):

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用.在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间.没有优化就被破坏了.复制/移动操作的省略,称为复制省略,在以下情况下允许(可以合并以消除多个副本):

- 在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象的名称(函数或catch子句参数除外),具有与函数返回类型相同的cv- unquali fi ed类型,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

- 在throw-expression中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会超出最内层封闭try-block的末尾(如果有的话)一),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象(15.1)的复制/移动操作

- 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-unquali fi ed类型的类对象时,可以通过将临时对象直接构造到该对象中来省略复制/移动操作.省略的复制/移动的目标

- 当异常处理程序的异常声明(第15节)声明一个相同类型的对象(cv-quali fi cation除外)作为异常对象(15.1)时,可以通过处理异常声明来省略复制/移动操作如果除了为exception-declaration声明的对象执行构造函数和析构函数之外,程序的含义将保持不变,则作为异常对象的别名.

[例子......省略]

复制/移动构造函数的语义就是这样,复制/移动对象的内容,同时初始化另一个对象的内容.如果您的复制构造函数向您的生日聚会发送带有邀请的电子邮件,那么如果您最终单独参加聚会,您不应该感到惊讶:)

好的,一些复制构造函数也做其他事情.想想智能指针的引用计数.但是,如果它被优化掉了,那很好.没有副本,也没有必要计算.


Kaz*_*gon 4

我相信您正在经历复制消除。因此,是的,这就是优化。

http://en.wikipedia.org/wiki/Copy_elision

在 C++ 计算机编程中,复制省略是指消除不必要的对象复制的编译器优化技术。C++ 语言标准通常允许实现执行任何优化,前提是生成的程序的可观察行为与程序完全按照标准的要求执行一样(即假装)。

该标准还描述了一些可以消除复制的情况,即使这会改变程序的行为,最常见的是返回值优化。

重点是我的。