C ++意外行为(我的临时在哪里? [英] C++ unexpected behaviour (where are my temporaries!?)

查看:132
本文介绍了C ++意外行为(我的临时在哪里?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个r值的实验,但是当gcc对我缺乏移动构造函数(我删除它),并没有落回到复制构造函数(如我所料)时,它突变
然后我从标记中删除-std = c ++ 11,尝试你看到下面,它有很多输出(最初没有),因为我试图找出为什么它不工作(我知道如何调试,但我发现stdout上的消息是一个很好的指示器发生的事情)

This was an r-value experiment but it mutated when gcc whined to me about lack of move-constructor (I'd deleted it) and didn't fall-back to the copy constructor (as I expected) I then removed -std=c++11 from the flags and tried what you see below, it has a lot of output (it didn't initially) because I am trying to work out why exactly it doesn't work (I know how to debug but I find messages on stdout to be a good indicator of something happening)

这是我的代码:

#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;
}

输出:

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

我使用解构,因为解构已经是一个字,有时候

I use deconstructing, because deconstruction is already a word, sometimes I use destructor, I'm never quite happy with the word, I favour destructor as the noun.

这是我的预期:

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.

我被称为die-hard C,我试图学习使用C ++比C与命名空间。

I have been called "die-hard C" and I am trying to learn to use C++ as more than "C with namespaces".

有人可能会说编译器优化了它我希望那个人永远不回答这个答案的问题现在还是永远,优化不能改变程序状态,它必须如同一切都按照规范说的那样发生,所以编译器可能会通过在cout上包含数字的消息来幽默,它可能不会甚至增加数量等等,但程序的输出将是一样的,如果它做了代码描述的一切。

Someone might say "the compiler optimises it out" I'd like that person never to answer a question with such an answer now or ever, optimisations must not alter the program state, it must be as if everything happened as the specification says, so the compiler may humor me by putting a message on cout that includes the number, it may not bother to even increase the number and such, but the output of the program would be the same as if it did do everything the code describes.

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

So it's not optimisations, what's going on?

推荐答案

这是一个优化,只有一个允许改变程序的可观察行为。

It is an optimization, the only one that is allowed to alter observable behaviour of a program.

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

Here's the paragraph 12.8./31, taken from standard draft n3337 (emphasis mine):


当满足某些条件时,允许实现省略类
对象的复制/移动构造,即使复制/移动构造函数和/
在这种情况下,
实现将省略的复制/移动操作的源和目标视为两个不同的
方法引用同一个对象,并且该对象的销毁发生在时间
的后面,当两个对象将在没有优化的情况下被销毁。
在下列情况下(可能结合
消除多个副本),允许复制/移动
操作,称为复制elision p>

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):


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

— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv- unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

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

— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object

- 当未被绑定到引用(12.2)的临时类对象被复制/移动时
到具有相同cv无参数类型的类对象,
可以省略复制/移动操作直接构造临时对象到省略的复制/移动的目标

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

- 当异常处理程序的异常声明(第15条)声明一个类型为
(除了cv-qualalion)作为异常对象(15.1)的对象时,复制/可以省略
通过将异常声明作为异常对象的别名来处理,如果程序
的含义不变,除了执行由
声明的对象的构造函数和析构函数异常声明。

— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.

[示例...省略]

[Example... omitted]

复制/移动构造函数的语义只是在初始化另一个对象时复制/移动对象的内容。如果你的复制构造函数发送带有邀请的电子邮件到你的生日聚会,你不应该惊讶,如果你最终单独聚会:)

The semantics of a copy/move constructor are just that, copying/moving the contents of an object while initializing another one. If your copy constructors send emails with invitations to your birthday party you should not be surprised if you end up partying alone :)

OK,一些复制构造函数做其他的事情。认为智能指针的引用计数。但如果这得到优化,没关系。没有副本,没有必要计算。

OK, some copy constructors do other things, too. Think reference counting of a smart pointer. But if that gets optimized away, it's fine. There was no copy and nothing needed to be counted.

这篇关于C ++意外行为(我的临时在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆