临时对象 - 是他们创造的时候,你是怎么认识他们的代码?临时对象 - 是他们创造的时候,你是怎么认识

2019-05-17 07:39发布

在埃克尔,第1卷,第:367

//: C08:ConstReturnValues.cpp
// Constant return by value
// Result cannot be used as an lvalue
class X {
   int i;
public:
   X(int ii = 0);
   void modify();
};

X::X(int ii) { i = ii; }

void X::modify() { i++; }

X f5() {
   return X();
}

const X f6() {
   return X();
}

void f7(X& x) { // Pass by non-const reference
   x.modify();
}

int main() {
   f5() = X(1); // OK -- non-const return value
   f5().modify(); // OK
// Causes compile-time errors:
//! f7(f5());
//! f6() = X(1);
//! f6().modify();
//! f7(f6());
} ///:~

为什么f5() = X(1)赢得成功? 这里发生了什么???

Q1。 当他X(1) -这到底是怎么回事呢? 这是一个构造函数调用-不应该在该再读取X::X(1); 它是类的实例-是不是类的实例是这样的: X a(1); 编译器如何确定哪些X(1)是? 我的意思..名装修发生这样.. X(1)构造函数调用会转化为类似: globalScope_X_int作为函数名.. ???

Q2。 当然临时对象用于存储所产生的对象X(1)创建,然后我就不会被那个然后分配给对象f5()返回(这也将是一个暂时的对象)? 鉴于f5()返回将很快被丢弃的临时对象,他怎么可以指定一个恒定的临时到另一个不变临时??? 有人能解释清楚为什么: f7(f5()); 应该以恒定的临时和不普通的旧reult f5();

Answer 1:

你所有的问题归结为C ++中的规则,它说,一个临时对象(一个没有名字)不能绑定到非const引用。 (因为斯特劳斯认为这可能会引发逻辑错误......)

一个难点,就是你可以在临时调用的方法:使X(1).modify()是不错,但f7(X(1))不是。

至于创建临时在那里,这是编译器的工作。 语言精确的是,临时应只能存活直到当前满表达(不再)的端部,其是用于类,它们的析构函数临时实例重要的规则具有副作用。

因此,以下语句X(1).modify(); 可以完全翻译成:

{
    X __0(1);
    __0.modify();
} // automatic cleanup of __0

考虑到这一点,我们可以攻击f5() = X(1); 。 这里我们有两种临时工,和转让。 分配的两个参数都必须充分评估的分配被调用之前,但为了不准确。 一个可能的翻译是:

{
    X __0(f5());
    X __1(1);
    __0.operator=(__1);
}

另一翻译交换,其中顺序__0__1被初始化

和键,它的工作是__0.operator=(__1)是一个方法调用,并且方法可以在临时对象被调用:)



Answer 2:

我并不完全答案满意,所以我在拍了一下:

“更有效的C ++”,斯科特迈尔斯。 第19项:“明白了临时对象的起源”

。 关于“临时变量”,好了,我怀疑与基督教劳直接指出,这是完全错误的布鲁斯Eckel的报道! 哎呀! 他是(Eckel的)把我们当做实验品! (这将是一本好书,对于像我这样的新手,一旦他改正自己的错误,所有)

迈尔:“在C ++中是看不见的真实临时对象 - 它们不会出现在源代码中的出现只要创建一个非堆对象,但没有点名这些无名的对象通常在两种情况之一发生:当隐式类型转换。应用进行函数调用成功,当函数返回的对象。”

“首先考虑在其中创建临时对象进行函数调用成功的情况下,出现这种情况时,传递给函数的对象的类型是不一样的参数,以它被绑定的类型。”

“这些转换传递对象时由值或传递给一个参考给const参数时才会发生。传递对象到参考到非const参数时,它们也不会发生”。

“在其下创建临时对象的第二组的情况是,当一个函数返回一个对象”。

“任何时候你看到一个参考给const参数,所述可能性是存在的一个临时将创建绑定到参数。每当你看到函数返回一个对象,一个临时将被创建(和后来破坏)”。

答案的另一部分中找到:“迈尔:有效的C ++”,在“导言”:

“复制构造是用来与相同类型的不同的对象初始化对象:”

String s1;       // call default constructor
String s2(s1);   // call copy constructor
String s3 = s2;  // call copy constructor

“也许拷贝构造函数的最重要的用途是定义什么意思传递和返回值的对象。”

关于我的问题:

f5() = X(1) //what is happening?

这里一个新的对象没有被初始化,故我这不是初始化(拷贝构造函数):这是一个任务(如马修中号指出)。

所述的临时创建因为按照迈耶(顶部段),这两个函数返回值,正在被创建,以便临时对象。 如马修指出使用伪代码,就变成: __0.operator=(__1)和一个逐位复制发生(由编译器完成)。

关于:

void f7(X& x);
f7(f5);

ERGO,暂时无法创建(迈尔:上面的段落)。 若是已经宣布: void f7(const X& x); 然后暂时将已经建立。

关于临时对象是一个常数:

迈耶说它(马修):“暂时将创建绑定到该参数。”

所以暂时只能被绑定到一个恒定的参考,它本身不是一个“常量”对象。

关于:什么是X(1)

迈耶Item27,有效C ++ - 3E,他说:

“C-风格转换是这样的:(T)表达式//投表达为类型Ť

函数式的管型使用以下语法:T(表达)//投表达是类型T的”

因此, X(1)是一个函数样式转换。 1表达被转换为类型X

梅耶又说它:

“关于我唯一一次使用老式的铸铁是,当我想打电话给一个明确的构造一个对象传递给一个函数,例如:

class Widget {
  public:
    explicit Widget(int size);
    ...
};

void doSomeWork(const Widget& w);
doSomeWork(Widget(15)); //create Widget from int
                        //with function-style cast

doSomeWork(static_cast<Widget>(15));

不知怎么的,故意的对象创建没有“感觉”像一个演员,所以我可能会使用函数式的,而不是铸的static_cast在这种情况下。”



Answer 3:

  1. 这确实是一个构造函数调用,一个表达式类型的临时对象X 。 形式的表达X([...])X是一个类型的名称是创建类型的临时对象的构造函数调用X (虽然我不知道该怎么解释,在适当的standardese,有特殊情况其中解析器可以表现不同)。 这是您在使用相同的结构f5f6功能,只是省略了可选的ii参数。

  2. 通过创建临时X(1)住(不获取破坏/无效),直到含有它的充分表达,这通常意味着(如在这种情况下,与所述赋值表达式),直到分号的末尾。 同样做f5创建一个临时的X ,并返回到调用点(内部main ),从而将其复制。 因此,在主要的f5调用也返回临时X 。 此临时X然后被分配临时X通过创建X(1) 在那之后做(和分号达到,如果你想),两者的临时被摧毁。 这种分配工作,因为这些函数返回普通的非恒定的对象,不管他们只是temprorary和销毁后的表达是充分评估(从而使分配或多或少毫无意义,即使完全有效)。

    它不工作f6 ,因为它返回一个const X到其上不能分配。 同样不f7(f5())不行,因为f5创建一个临时的和暂时的对象不绑定到非const左值引用X& (C ++ 11引入了右值引用X&&为了这个目的,但是这是一个不同的故事)。 它是否会工作f7了一个const引用const X&为常量左值引用绑定到临时对象(但随后f7本身将不再起作用,当然)。



Answer 4:

下面是当你执行你的代码到底发生了什么的例子。 我做了一些修改,以澄清幕后的过程:

#include <iostream>

struct Object
{
    Object( int x = 0 ) {std::cout << this << ": " << __PRETTY_FUNCTION__ << std::endl;}
    ~Object() {std::cout << this << ": " << __PRETTY_FUNCTION__ << std::endl;}
    Object( const Object& rhs ){std::cout << this << ": " << __PRETTY_FUNCTION__ << " rhs = " << &rhs << std::endl;}
    Object& operator=( const Object& rhs )
    {
        std::cout << this << ": " << __PRETTY_FUNCTION__ << " rhs = " << &rhs << std::endl;
        return *this;
    }
    static Object getObject()
    {
        return Object();
    }
};

void TestTemporary()
{
    // Output on my machine
    //0x22fe0e: Object::Object(int) -> The Object from the right side of = is created Object();
    //0x22fdbf: Object::Object(int) -> In getObject method the Temporary Unnamed object is created
    //0x22fe0f: Object::Object(const Object&) rhs = 0x22fdbf -> Temporary is copy-constructed from the previous line object
    //0x22fdbf: Object::~Object() -> Temporary Unnamed is no longer needed and it is destroyed
    //0x22fe0f: Object& Object::operator=(const Object&) rhs = 0x22fe0e -> assignment operator of the returned object from getObject is called to assigne the right object
    //0x22fe0f: Object::~Object() - The return object from getObject is destroyed
    //0x22fe0e: Object::~Object() -> The Object from the right side of = is destroyed Object();

    Object::getObject() = Object();
}

你要知道,在大多数现代编译器的拷贝构造将被避免。 这是因为由编译器它是由优化(返回值优化)。 在我的输出我已经显式删除优化展现什么是真正按照标准发生。 如果要去除这项优化过使用下面的选项:

-fno-elide-constructors


文章来源: Temporary objects - when are they created, how do you recognise them in code?