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

В Eckel, Vol 1, pg: 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); Является ли это экземпляром classа – не является экземпляром экземпляра что-то вроде: X a(1); Как компилятор определяет, что такое X(1) ? Я имею в виду, что украшение имен происходит так. X(1) вызов конструктора переводится на что-то вроде: globalScope_X_int как имя функции .. ???

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

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

    Один улов – это то, что вы можете вызывать метод на временном: так что X(1).modify() отлично, но f7(X(1)) не является.

    Что касается временного создания, это задание компилятора. Правила языка точные, что временное должно существовать только до конца текущего полного выражения (и не более), что важно для временных экземпляров classов, деструктор которых имеет побочный эффект.

    Поэтому следующий оператор 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 порядок, в котором __0 и __1 инициализируются )

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

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

    «Более эффективный C ++», Скотт Мейерс. Пункт 19: «Понять происхождение временных объектов»

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

    Meyer: «True временные объекты в C ++ невидимы – они не появляются в вашем исходном коде, они возникают всякий раз, когда создается объект, не содержащий кучу, но не названный. Такие неназванные объекты обычно возникают в одной из двух ситуаций: когда неявные преобразования типов применяются для успешного выполнения вызовов функций и возврата функций объектам ».

    «Сначала рассмотрим случай, когда временные объекты создаются для успешного выполнения вызовов функций. Это происходит, когда тип объекта, переданный функции, не совпадает с типом параметра, к которому он привязан».

    «Эти преобразования происходят только при передаче объектов по значению или при переходе к параметру reference-to-const. Они не возникают при передаче объекта в параметр reference-to-non-const».

    «Второй набор обстоятельств, при которых создаются временные объекты, – это когда функция возвращает объект».

    «В любое время, когда вы видите параметр reference-to-const, существует вероятность того, что для привязки к этому параметру будет создано временное. В любое время, когда вы видите функцию, возвращающую объект, временное создание будет (и позже уничтожено)».

    Другая часть ответа находится в: «Meyer: Effective C ++», в «Введение»:

    «конструктор копирования используется для инициализации объекта другим объектом того же типа:«

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

    «Вероятно, наиболее важным использованием конструктора копирования является определение того, что означает передать и вернуть объекты по значению».

    Что касается моих вопросов:

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

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

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

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

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

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

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

    Мейер говорит об этом (и Маттье): «временное создание будет связано с этим параметром».

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

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

    Meyer, Item27, Effective C ++ – 3e, он говорит:

    «Стили C-стиля выглядят следующим образом: (T) выражение // выражение для выражения типа T

    Функция cast-cast использует этот синтаксис: 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(15)); 

    Так или иначе, намеренное создание объекта не «чувствует» себя как актерский состав, поэтому в этом случае я, вероятно, использовал бы стиль функции, а не static_cast ».

    1. Это действительно вызов конструктора, выражение, оценивающее временный объект типа X Выражения формы X([...]) с X являющимся именем типа, являются конструкторскими вызовами, которые создают временные объекты типа X (хотя я не знаю, как объяснить это в правильном стандартном виде, и есть особые случаи где парсер может вести себя по-разному). Это та же конструкция, которую вы используете в своих функциях f5 и f6 , просто опустив необязательный аргумент ii .

    2. Временное созданное X(1) живет (не получает деструкции / недействительно) до конца полного выражения, содержащего его, что обычно означает (как в данном случае с выражением присваивания) до точки с запятой. Аналогичным образом f5 создает временный X и возвращает его на сайт вызова (внутри main ), копируя его. Таким образом, в основном вызов f5 также возвращает временный X Затем этому временному X присваивается временный X созданный X(1) . После этого (и точка с запятой достигнута, если вы хотите), оба временных объекта будут уничтожены. Это назначение работает, потому что эти функции возвращают обычные непостоянные объекты, независимо от того, являются ли они просто временными и уничтоженными после того, как выражение полностью оценено (тем самым делая назначение более или менее бессмысленным, хотя и совершенно корректным).

      Он не работает с f6 поскольку возвращает const X на который вы не можете назначить. Точно так же f7(f5()) не работает, так как f5 создает временные и временные объекты, которые не привязываются к неконстантным lvalue-ссылкам X& (C ++ 11 вводил rvalue ссылки X&& для этой цели, но это совсем другая история). Это сработает, если f7 примет константу const const X& , поскольку константные ссылки lvalue привязаны к временным (но тогда, конечно, сам f7 больше не будет работать).

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

     #include  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 
    Interesting Posts

    В Xcode 7 я все время получаю ошибку «Нет учетных записей при доступе к iTunes-подключению»

    Лучше ли вызывать ToList () или ToArray () в запросах LINQ?

    Какие маршрутизаторы вы предпочитаете для DD-WRT или OpenWRT?

    SwingWorker не обновляет JProgressBar без Thread.sleep () в пользовательской диалоговой панели

    java.lang.IllegalArgumentException: указанный основной набор ресурсов недействителен

    Как выполнять команды на уровне root в Linux (Fedora) Live Media

    Как правильно сравнить два целых числа в Java?

    Анализ URL-адреса в JavaScript

    Прослушивание переменных изменений в JavaScript

    Включение / выключение состояния сеанса для каждого controllerа / метода действий

    Как получить информацию о типе файла на основе расширения? (не MIME) в c #

    Расчет полосы пропускания

    i = i ++ действительно неопределенное поведение?

    Windows 7 поставляется с чем-то вроде Virtual PC?

    Как я могу переопределить DNS-серверы Windows10 по умолчанию для использования DNS-серверов, назначенных OpenVPN

    Давайте будем гением компьютера.