Вопрос по c++ – Временные объекты - когда они создаются, как вы узнаете их в коде?

30

В Экель, том 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) succed? Что здесь происходит???

Q1. Когда он делаетX(1) - что происходит здесь? Это вызов конструктора - не должен это читатьX::X(1); Реализация класса - это не класс? создание экземпляра что-то вроде:X a(1); Как компилятор определяет, что X(1) является?? Я имею в виду .. название украшения происходит так ..X(1) конструктор вызов будет переводить что-то вроде:globalScope_X_int как функция название.. ???

Q2. Конечно, временный объект используется для хранения результирующего объекта, которыйX(1) создает и затем будет назначен объектуf5() возвращается (который также будет временным объектом)? При условииf5() возвращает временный объект, который вскоре будет отброшен, как он может назначить одну постоянную временную к другому постоянному временному ??? Может ли кто-нибудь объяснить, почему: f7(f5()); должен приводить к постоянному временному, а не старомуf5();

X :: X ссылается на конструктор. это не разрешено в выражениях, но только в некоторых формах объявлений. конструкторalways вызывается неявно. Johannes Schaub - litb

Ваш Ответ

4   ответа
6
  1. This is indeed a constructor call, an expression evaluating to a temporary object of type X. Expressions of the form X([...]) with X being the name of a type are constructor calls that create temporary objects of type X (though I don't know how to explain that in proper standardese, and there are special cases where the parser can behave differently). This is the same construct you use in your f5 and f6 functions, just omitting the optional ii argument.

  2. The temporary created by X(1) lives (doesn't get destructed/invalid) until the end of the full expression containing it, which usually means (like in this case with the assignment expression) until the semicolon. Likewise does f5 create a temporary X and return it to the call site (inside main), thus copying it. So in main the f5 call also returns a temporary X. This temporary X is then assigned the temporary X created by X(1). After that is done (and the semicolon reached, if you want), both temporaries get destroyed. This assignment works because those functions return ordinary non-constant objects, no matter if they are just temprorary and destroyed after the expression is fully evaluated (thus making the assignment more or less senseless, even though perfectly valid).

    It doesn't work with f6 since that returns a const X onto which you cannot assign. Likewise does f7(f5()) not work, since f5 creates a temporary and temporary objects don't bind to non-const lvalue references X& (C++11 introduced rvalue references X&& for this purpose, but that's a different story). It would work if f7 took a const reference const X&, as constant lvalue references bind to temporaries (but then f7 itself wouldn't work anymore, of course).

@paleywiener Нет, ониnot автоматически константа, поэтому это назначение работает (и почему есть дополнительная функцияf6 который возвращаетconstгде будет разница междуf5 а такжеf6 иначе?). Я не знаю, если это вырвано из контекста, но ваша цитата просто неверна. Или, может быть, он просто советует вам сделать все возвращаемые значения константами самостоятельно.
я думаю, что это неправильно - «Это назначение работает, потому что эти функции возвращают обычные непостоянные объекты, независимо от того, являются ли они просто временными»
Eckel, pg: 368: «Но у временных людей есть одна вещь: они автоматически постоянны» & quot; компилируя все временные значения автоматически, компилятор информирует вас, когда вы совершаете эту ошибку & quot;
1

Вот пример того, что на самом деле происходит, когда вы выполняете свой код. Я сделал несколько модификаций, чтобы прояснить процессы за сценой:

#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
31

Все ваши вопросы сводятся к правилу в C ++, которое гласит, что временный объект (тот, у которого нет имени) не может быть связан с неконстантной ссылкой. (Потому что Страуструп чувствовал, что это может вызвать логические ошибки ...)

Одна загвоздка в том, что вы можете вызывать метод временно: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);
}

(the other translation is swapping the order in which __0 and __1 are initialized)

И ключом к этому является то, что__0.operator=(__1) является вызовом метода, и методы могут быть вызваны для временных файлов :)

@paleywiener Временныйnot постоянный объект, который всегда был нашими ответами. Если это то, чтоEckel говорит (хотя я сомневаюсь в этом), он простоwrong.
Я имею в виду, что это был мой вопрос все время ... временный объект является постоянным объектом, и все же Экель, кажется, весело назначает его после того, как он был помечен компилятором как const !!!
@paleywiener Те,__0 вещи в его коде представляют собой создаваемые временные переменные, они просто для демонстрации того, как на самом деле работает код.
М: да! временный это постоянный объект! но обязательно X __0 (f5 ()); будет также недопустимым, если __0 () не объявлен как __0 (const X & amp;) ?? Функция-член должна пообещать не изменять то, что ей передается! Не могли бы вы уточнить, что такое __0 и __1 (ссылка)? Я не знаю кто они. Кроме того, я не понимаю, что вы подразумеваете под "методами, которые могут вызываться для временных". Временный объект является константным объектом ... поэтому, если метод, который вы вызываете, не обещает его изменить ...
@paleywiener Быть постоянным и быть временным полностью ортогонально.
11

Я не был полностью удовлетворен ответами, поэтому я взглянул на:

"More Effective C++", Scott Meyers. Item 19: "Understand the origin of temporary Objects"

, Относительно освещения Брюсом Экелом «Temporaries», ну, как я подозреваю и как прямо указывает Кристиан Рау, это ясно неправильно! Хмм! Он (Экель) использует нас как морских свинок !! (это было бы хорошая книга для новичков вроде меня, как только он исправит все свои ошибки)

Meyer: "True temporary objects in C++ are invisible - they don't appear in your source code. They arise whenever a non-heap object is created but not named. Such unnamed objects usually arise in one of two situations: when implicit type conversions are applied to make function calls succeed and when functions return objects."

"Consider first the case in which temporary objects are created to make function calls succeed. This happens when the type of object passed to a function is not the same as the type of the parameter to which it is being bound."

"These conversions occur only when passing objects by value or when passing to a reference-to-const parameter. They do not occur when passing an object to a reference-to-non-const parameter."

"The second set of circumstances under which temporary objects are created is when a function returns an object."

"Anytime you see a reference-to-const parameter, the possibility exists that a temporary will be created to bind to that parameter. Anytime you see a function returning an object, a temporary will be created (and later destroyed)."

Другая часть ответа находится в: «Мейер: эффективный C ++», в & quot; Введение & quot ;:

"a copy constructor is used to initialize an object with a different object of the same type:"

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

"Probably the most important use of the copy constructor is to define what it means to pass and return objects by value."

По поводу моих вопросов:

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

Здесь новый объект не инициализируется, следовательно, это не так инициализация (конструктор копирования): это назначение (как Матье М указал).

Временные создаются потому, что, согласно Мейеру (верхние абзацы), обе функции возвращают значения, поэтому создаются временные объекты. Как указал Матье, используя псевдокод, он становится: __0.operator=(__1) и происходит побитовое копирование (сделано компилятор).

Что касается:

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

следовательно, временное не может быть создано (Мейер: верхние абзацы). Если это было объявлено:void f7(const X& x); тогда временный были созданы.

Относительно временного объекта, являющегося константой:

Мейер говорит это (и Матье): «Будет создан временный объект, чтобы связать это . Параметр & Quot;

Таким образом, временный объект связан только с постоянной ссылкой и сам по себе не является & quot; const & quot; объект.

Что касается: что такоеX(1)?

Мейер, Item27, Effective C ++ - 3e, он говорит:

"C-style casts look like this: (T)expression //cast expression to be of type T

Function-style casts use this syntax: T(expression) //cast expression to be of type T"

ТакX(1) это приведение в стиле функции.1 выражение приводится к типX.

И Мейер говорит это снова:

"About the only time I use an old-style cast is when I want to call an explicit constructor to pass an object to a function. For example:

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));

Somehow, deliberate object creation doesn't "feel" like a cast, so I'd probably use the function-style cast instead of the static_cast in this case."

X (1) не является актером. Поместите оператор печати в конструктор X, и вы увидите, что он выполняется. Символы и ключевые слова в C +++ (и C) обычно меняют свое значение в зависимости от содержимого.
Я не вижу, каким образом этот "ответ" объясняет вещиasked in the question на которые нет ответа в других ответах. Если вы хотите знать больше вещей, чем задаваемые вопросы,ask them.

Похожие вопросы