среда, января 09, 2008

Возможно, самый важный const

Герб Саттер у себя в блоге рассказывает про интересный случай с использованием const.

Краткий пересказ для ленивых.

string f() { return "abc"; }

void g() {
const string& s = f();
cout << s << endl; // можно ли использовать временный объект?
}


Код несколько напрягает. Создается ссылка на временный объект. Но тем не менее, с кодом все в порядке. Почему так? Потому что в С++ явно специфицировано, что если привязать временный объект к ссылке на const в стеке, то жизнь временного объекта будет продлена. Теперь он будет жить столько, сколько живет константная ссылка на него. В приведенном примере все валидно, время жизни s заканчивается с закрывающей фигурной скобкой.

Это все относится только к объектам в стеке. На члены класса это не действует.

С++ это специфицирует, а как оно в реальности, работает? Герб проверил в нескольких компиляторах, нормально, практически во всех работает.

Легким движением руки убираем const...

string f() { return "abc"; }

void g() {
string& s = f(); // все еще нормально?
cout << s << endl;
}


И получаем невалидный код, наличие const'а тут важно. Правильный компилятор выдаст ошибку на этапе компиляции.

И есть еще момент с вызовом деструктора.

Derived factory(); // construct a Derived object

void g() {
const Base& b = factory(); // здесь вызов Derived::Derived
// … используем b …
} // здесь вызывается Derived::~Derived напрямую
//-- а не Base::~Base + virtual dispatch!


Ссылки по теме:
Использование const. Часть 1.
Использование const. Часть 2.

52 комментария:

  1. В примере без const время жизни s тоже ведь заканчивается с закрывающей фигурной скобкой?

    Этот код работает в Visual Studio, никаких warnings нет (кроме getch()):

    std::string f()
    {
    return "abc";
    }

    void g()
    {
    using namespace std;

    const string& s1 = f();
    cout << "Reading with const: " << s1 << endl;

    string& s2 = f();
    cout << "Reading without const: " << s2 << endl;

    getch();
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
    g();
    }

    ОтветитьУдалить
  2. Анонимный9/1/08 10:23

    Совершенно правильно, что ругают C++! Поделом. :-)

    ОтветитьУдалить
  3. совершенно верно все Алёна написала

    В случае
    string& s=string("abc")
    создается ссылка на временную переменную, которая прекращает свое существование после выполнения выражения. Если бы так не было (т.е. время жизни временной переменной равнялось времени жизни ссылки на нее), то можно было бы написать

    string& s=string("abc")
    s="Hello world"

    что однако не имеет большого смысла - модифицировать скоро исчезающую временную переменную

    В случае
    const string& s=string("abc")
    временная переменная живет пока живет константная ссылка на нее, и так как ссылка константная, то модификация временной переменной запрещена.

    Мне кажется именно из этих соображений исходили, когда определяли такой behaviour

    ОтветитьУдалить
  4. 2Roman Konovalov:
    В примере без const время жизни s тоже ведь заканчивается с закрывающей фигурной скобкой?

    Этот код работает в Visual Studio, никаких warnings нет


    Я проверила в MSVC++6.0 и в MSVC++2003.
    MSVC++6.0 - все компиляет на ура. При запуске никаких проблем.
    MSVC++2003 - выдает warning, если ему поднять уровень тревожности до четвертого.
    warning C4239: nonstandard extension used : 'initializing' : conversion from 'std::string' to 'std::string &'
    A reference that is not to 'const' cannot be bound to a non-lvalue

    При запуске опять же никаких проблем.

    Саттер у себя в посте упомянул, что VC++ компиляет это дело с ворнингом. То, что в шестой версии ворнинга нет - это неправильно, потому что это не валидный ISO C++. Но шестой VC++ он вообще довольно глюкавый.

    Итого: нет, без const время жизни s заканчивается до фигурной скобки, приведенный в примере без const код не валиден. VC++ при компиляции этого примера допускает некоторую самодеятельность, в принципе можно этой его особенностью пользоваться, если очень нужно, но помнить, что код при этом будет непортируем.

    2blog:
    Совершенно правильно, что ругают C++! Поделом. :-)

    Злыдень :-)

    ОтветитьУдалить
  5. Если временая переменая прекращает свое существование сразу после string& s=string("abc"), то что там будет в s к концу выполнения функции никто не знает. Тем не менее, Visual Studio такие вещи не отлавливает. Либо дело кончится крэшом, либо эта конкретная реализация компилятора не освобождает временную переменную сразу. Получается так?

    ОтветитьУдалить
  6. 2 Алёна - верно, с 4ым уровнем показывает варнинг. MSDN объясняет так же, как и в статье: http://msdn2.microsoft.com/en-us/library/aa233872(VS.60).aspx

    Только пример на установленом на моей машине MSDN другой - :

    // compile with: /W4 /c
    struct C { C(){} };

    void func(void) {
    C & rC = C(); // C4239
    const C & rC2 = C(); // OK
    rC2;
    }

    Век живи, век учись, хорошая статья :-)

    ОтветитьУдалить
  7. gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
    выдает ошибку времени компиляции на этом примере ;-).

    ОтветитьУдалить
  8. либо эта конкретная реализация компилятора не освобождает временную переменную сразу
    Получается так, раз Алёна говорит, что при запуске никаких проблем ;-)

    ОтветитьУдалить
  9. 2Roman Konovalov:
    Либо дело кончится крэшом, либо эта конкретная реализация компилятора не освобождает временную переменную сразу. Получается так?

    Скорее второе - MSVC++ не удаляет временную переменную сразу в этом случае. Причем, судя по тому, что это задокументировано в msdn, это фича MSVC++, а не бага.

    ОтветитьУдалить
  10. по хорошему надо было бы добавить опцию, которая эту "фичу" позволяет отключать ;-). Подобные "фичи" очень мешают, когда нужно писать и отлаживать переносимый код :-(

    ОтветитьУдалить
  11. Анонимный10/1/08 18:42

    Вы чё-та все miss the point
    почитайте ка лучше оригинал. Если не поймете, то хотя бы не рассуждайте про C++ -- у этой фичи тож есть своя история.
    Тут фишка в том как это работает. Заодно посмотрите в каком контесте Александерску сказал про "самый главный const"

    ОтветитьУдалить
  12. Анонимный13/1/08 14:41

    спасибо за информацию.

    как раз из тех, что сам даже не подумаешь искать, будучи уверен, что так нельзя

    ОтветитьУдалить
  13. Анонимный14/1/08 16:38

    По мне - информация интересная, но ничего нового, потому что временные переменные и их константность - азы С++. Если учесть, что по ссылкам переменные передаются только через модификатор const, и чувствуют себя при этом прекрасно - а параметры функции те же самые локальные переменные в контексте этой самой функции, то... :) Это ж логика, которая должна работать на уровне подсознания. Я, например, долго напрягался, пытаяся найти трик в примере, пока не прочитал пост. Зато полезна информация в отношении политики MSVC. Опять огорчения :)

    К комментарию blog: это как правила вождения автомобиля - если знаешь простое правило, что направо поворачивать надо с правой полосы, то и на кольце не запутаешься, вовремя перестроившись. Будем ругаться, что в правилах отдельно не оговаривается поведение водителя на кольце? Так же и в С++, все достаточно грамотно и лаконично, я вообще проблем не испытываю и не удивляюсь в большинстве случаев, при том что стандарта не читал и не собираюсь читать. Удивляет порой поведение компиляторов, которые, чувствуешь, что гонят уже на подсознательном уровне. Это откровенно раздражает, такие не соответствия.

    ОтветитьУдалить
  14. 2hVostt:
    по ссылкам переменные передаются только через модификатор const, и чувствуют себя при этом прекрасно - а параметры функции те же самые локальные переменные в контексте этой самой функции

    Нет, это не так. В функцию в качестве параметра может быть передана неконстантная ссылка.

    ОтветитьУдалить
  15. Не совсем понятно, на кой черт это нужно. На практике

    string f() { return "abc"; }

    ...

    string a = f();

    будет работать в точности так же. Да?

    ОтветитьУдалить
  16. Анонимный16/1/08 21:07

    2Алёна: кажется речь шла о временных переменных. Или с каких-то пор разрешено временные переменные передавать в функции по неконстантным сслыкам? :) У меня не получается. VC 7/8/9.

    а еще, зачем так много кода? можно выразить и одной строчкой:

    const int& i = 5;

    ОтветитьУдалить
  17. Анонимный16/1/08 21:11

    [b]zorgg[/b] обычно константностью не злоупотребляют, обычно как раз наоборот - её игнорируют.

    а работать ваш код будет так как и ожидается :) т.е. копирование по значению (если не обращать внимание на особенности реализации std::string).

    ОтветитьУдалить
  18. Вы ошибаетесь.

    Создайте класс с копирующим конструктором, добавьте в него (в к-конструктор, а не в класс) отладочный вывод, попробуйте.

    Подавляющее большинство компиляторов будет генерировать код, работающий напрямую с lvalue.

    Я сегодня обсуждал это со своим наставником, по его словам коммитет принял, после длительного обсуждения, решение, при котором компилятор может опускать копирующий конструктор, если ему так нужно. Вывод - избегать сайд-эффектов в копирующих конструкторах.

    ОтветитьУдалить
  19. Что бы не быть голословным,

    #include <stdio.h>

    class Supergrass
    {
    int a;
    public:
    Supergrass(int a):a(a) {puts("ctor.");}
    ~Supergrass(){puts("dtor.");}
    Supergrass(const Supergrass &a) {puts("copy.");}
    };

    Supergrass func()
    {
    return Supergrass(42);
    }

    int main()
    {
    puts("before decl.");
    Supergrass x = func();
    puts("after decl.");
    return 0;
    }

    Если компилятор не дурак, то вывод будет следуюющим:

    before decl.
    ctor.
    after decl.
    dtor.

    Что и требовалось.

    ОтветитьУдалить
  20. Анонимный17/1/08 15:22

    Продлевать жизнь объекта при бинде его на константную ссылку стандарт не обязывает (кроме случая случая с передачей аргумента в копирующем конструкторе). Может создаться временная копия объекта, и тогда именно эта копия, а не оригинальный объект, будет доступна по ссылке.

    ОтветитьУдалить
  21. 2zorgg:
    компилятор может опускать копирующий конструктор, если ему так нужно

    Угу, return value optimization. Там только не "если нужно", в Стандарте (12.8/15) есть жесткие ограничения когда комплиятор может такую оптимизацию делать.

    будет работать в точности так же. Да?

    Нет. Я помедитировала над Стандартом, поспрашивала Гугл и вот что получается.

    В примере Саттера
    const string& s = f();

    продлевается время жизни временной переменной в стеке.

    В твоем примере
    string a = f();
    временная переменная не создается вовсе. Компилятор видит, что возвращаемый функцией f локальный объект используется при инициализации и он создает этот возращаемый объект прямо в a. Поэтому и не вызывается конструктор копирования. Тут вообще никакого копирования не происходит, даже побитового.

    2hVostt
    кажется речь шла о временных переменных.

    Я привела точную цитату из твоего комментария и отвечала на нее.


    а еще, зачем так много кода? можно выразить и одной строчкой:

    const int& i = 5;


    Изначально вопрос был такой: есть пример кода, корректен ли он.

    ОтветитьУдалить
  22. 2Adept:
    Продлевать жизнь объекта при бинде его на константную ссылку стандарт не обязывает

    В 12.2/5 приводится пример, где говорится следующее

    The temporary T3 bound to the reference cr is destroyed
    at the end of cr’s lifetime, that is, at the end of the program.

    так что обязывает, имхо.

    ОтветитьУдалить
  23. 2Алёна: Да я просто пытаюсь понять, зачем это вообще может быть нужно. Какое-либо вменяемое практическое применение.

    ОтветитьУдалить
  24. 2zorgg:

    Польза в том, что тип ссылки контравариантен типу значения. Что позволяет играть с полиморфизмом (не упоминая фактический тип).

    const base& x = make_derived();

    Причём этот полиморфизм - времени компиляции, а не только времени исполнения. То есть, нет накладных расходов.

    Этакое auto для бедных.

    Где это используется: в основном, там, где make_... перегружено.

    Например,

    struct scoped_base {
    operator bool() const { return false; }
    };

    template<class Mutex>
    struct scoped_lock_t : scoped_base
    {
    Mutex& m_;
    scoped_lock_t(Mutex& m) : m_(m) { take(m_); }
    ~scoped_lock_t() { give(m_); }
    };
    template<class Mutex>
    scoped_lock_t<Mutex> scoped_lock(Mutex& m)
    { return scoped_lock_t<Mutex>(m); }

    #define LOCKED(m) \
    if(scoped_base& sb = scoped_lock(m)) {} else

    .....
    LOCKED(the_critical_section)
    {
    .....
    }

    LOCKED(the_semaphore)
    {
    .....
    }

    ОтветитьУдалить
  25. 2zorgg:
    Да я просто пытаюсь понять, зачем это вообще может быть нужно. Какое-либо вменяемое практическое применение.

    Вот пример, который привел Саттер:
    ScopeGuard, написанный Александреску.

    ОтветитьУдалить
  26. Анонимный30/1/08 01:49

    Алёна:
    В 12.2/5 приводится пример, где говорится следующее

    The temporary T3 bound to the reference cr is destroyed
    at the end of cr’s lifetime, that is, at the end of the program.

    На самом дле это означает лишь то, что некий временный объект будет существовать до конца времени жизни cr, но это может быть и копия другого временного объекта. Даже если компилятор в действительности не создаст копии, по стандарту мы всё равно обязаны обеспечить возможность вызова копирующего конструктора (The constructor that would be used to make the copy shall be callable whether or not the copy is actually done - 8.5.3/5)

    ОтветитьУдалить
  27. В свое время с удивлением обнаружил, что все классы исключений из stdexcept принимают в конструкторе const std::string&, а не "честный" std::string; То есть следует полагать, что объект класса исключения не копирует строку к себе.

    Этот факт заставил меня писать что-то вроде этого:
    if (badSituation) {
    static string s = "Bad situation";
    throw std::logic_error(s);
    }

    Исходя из этой статьи получается, что
    if (badSituation) {
    throw std::logic_error("Bad situation");
    }
    тоже будет вполне корректно....

    А вот если нужно строку прежде сформировать (допустим, записать пару интов), то все-равно прийдется париться с обеспечением адекватного времени жизни =( .

    ОтветитьУдалить
  28. Анонимный17/11/08 11:13

    От чего const не сохраняет объект в этом случае?

    std::ostringstream oss;
    oss << "AAA";
    const char* const& tmp = oss.str().c_str();

    //далее tmp содержит невалидное значение

    ОтветитьУдалить
  29. Отчего const не оставляет объект?
    А отчего бы ему оставлять.

    oss.str() возвращает временный объект, время жизни которого - до точки с запятой.

    Далее, берём оттуда указатель на внутренние данные этого объекта.

    Сам объект никак не продлеваем. Вот и наступает "фиаско".

    А вот если бы написать

    string const& s = oss.str();
    char const* tmp = s.c_str();

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

    ОтветитьУдалить
  30. Анонимный25/11/08 16:21

    Видимо работает только для объектов, т.к. такой вариант не работает:

    int main()
    {
    std::string str1 = "AAA";
    std::string str2 = "BBB";
    char const* const & cstr = (str1 + str2).c_str();
    printf("\n%s\n", cstr);
    return 0;
    }

    ОтветитьУдалить
  31. 2Аноним
    Видимо работает только для объектов, т.к. такой вариант не работает:

    Это какая-то вера в магические заклинания "iddqd" и "const&"?

    Вариант работает. Но не так, как вам хочется.

    Поскольку (s1+s2) - это временный объект, то .c_str() возвращает указатель на временные данные.
    Дальше - сам указатель остаётся жить, а данные помирают.

    С равным успехом можно было взять итератор - (s1+s2).begin() - очень даже объект какого-то там класса.
    Но этот объект не озадачивается глубоким копированием данных, связанных с ним.
    И указатель не озадачивается.

    ОтветитьУдалить
  32. Анонимный27/11/08 15:13

    В следующем коде что не так?

    #include <iostream>
    #include <map>

    typedef std::map<int, int> mapIntT;
    mapIntT MapInt;

    int main()
    {
    MapInt.insert(std::pair<int, int>(1, 2));
    mapIntT::iterator const& cIt = --MapInt.end();
    if (cIt->second == cIt->first)
    std::cout << "equal\n";
    return 0;
    }

    В данном случае итератор - это объект. Он должен сохраняться, поскольку ссылка создается константная, или я чего-то недопонимаю?

    ОтветитьУдалить
  33. "Поскольку ссылка константная" не является причиной для продлевания жизни. Ещё раз: "const&" не IDDQD.

    Давайте посмотрим, что происходит в коде --MapInt.end()

    MapInt.end() возвращает rvalue - итератор на конец.

    С++ позволяет вызывать неконстантные методы у rvalue; вот если бы автодекремент был внешней функцией, то здесь мы получили бы ошибку.

    Итак, вызывается
    iterator& iterator::operator--()
    {.....; return *this; }
    который возвращает неконстантную ссылку на самого себя.

    Но компилятору-то уже пофиг, что это был временный объект. Сказано вернуть ссылку, нате.

    Таким образом,
    iterator const& clt = --MapInt.end();
    неконстантная ссылка превращается в константную.

    Дальше мы выходим за пределы полного выражения, временный объект погибает, ссылка инвалидируется.

    Отчасти проблему могут решить ссылки-на-rvalue (из стандарта C++0x).
    Для этого итератор должен иметь два оператора автодекремента
    iterator& operator--(iterator&);
    iterator&& operator--(iterator&&);
    (Я ещё не вкурил в новый стандарт, как это добро сделать членами; а на внешних функциях - вот так).

    ОтветитьУдалить
  34. AlenaC++, может быть, перевести дискуссию из гостевухи в форум? Уж больно неудобно обсуждать код в "комментариях", изначально неприспособленных для этого.

    Предлагаю родную для меня площадку - RSDN. http://www.rsdn.ru, форум С++.

    ОтветитьУдалить
  35. 2Kodt:

    AlenaC++, может быть, перевести дискуссию из гостевухи в форум? Уж больно неудобно обсуждать код в "комментариях", изначально неприспособленных для этого.

    Предлагаю родную для меня площадку - RSDN. http://www.rsdn.ru, форум С++.


    Эээ... Переносите :-)
    А от меня какие действия требуются?

    ОтветитьУдалить
  36. Собственно, никаких :)
    Просто было бы невежливо с моей стороны сделать это в обход хозяина блога.
    ---
    Приглашаю всех заинтересованных участников продолжить обсуждение на RSDN, с любезного согласия Алёны.
    http://www.rsdn.ru/Forum/?mid=3191155

    ОтветитьУдалить
  37. а такой код валиден?

    int& f() { int a = 1; return a; }

    void g() {
    const int& b = f();
    cout << b << endl;
    }

    ОтветитьУдалить
  38. 2 zmeysa:

    а такой код валиден?

    int& f() { int a = 1; return a; }


    Меня напрягает уже вот эта строка, поскольку мы здесь пытаемся вернуть ссылку на локальную переменную.

    Comeau на это выдает вполне ожидаемый warning.

    warning: returning reference to local variable

    ОтветитьУдалить
  39. Анонимный16/1/09 21:50

    Алён, а вот интересно, ты сама как думаешь, что такое ссылка в стеке, если ссылка вообще не объект по стандарту? ;)
    Кроме того, насколько я понял стандарт, дело не в способе размещения(стек тут не причем), а в константности ссылки. Вот из стандарта(2003) пример(12.5/5):
    [Example:
    class C {
    // ...
    public:
    C();
    C(int);
    friend C operator+(const C&, const C&);
    ˜C();
    };
    C obj1;
    const C& cr = C(16)+C(23);
    C obj2;

    the expression C(16)+C(23) creates three temporaries. A first temporary T1 to hold the result of the
    expression C(16), a second temporary T2 to hold the result of the expression C(23), and a third tempo-
    rary T3 to hold the result of the addition of these two expressions. The temporary T3 is then bound to the
    reference cr. It is unspecified whether T1 or T2 is created first. On an implementation where T1 is cre-
    ated before T2, it is guaranteed that T2 is destroyed before T1. The temporaries T1 and T2 are bound to
    the reference parameters of operator+; these temporaries are destroyed at the end of the full expression
    containing the call to operator+. The temporary T3 bound to the reference cr is destroyed at the end of
    cr’s lifetime, that is, at the end of the program. In addition, the order in which T3 is destroyed takes into
    account the destruction order of other objects with static storage duration. That is, because obj1 is con-
    structed before T3, and T3 is constructed before obj2, it is guaranteed that obj2 is destroyed before T3,
    and that T3 is destroyed before obj1. ]

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

    Д.К.

    ОтветитьУдалить
  40. 2Анонимный:

    Кроме того, насколько я понял стандарт, дело не в способе размещения(стек тут не причем), а в константности ссылки.

    По-моему, стек там упоминается по отношению к объекту, жизнь которого продлевается, а не по отношению к ссылке.

    ОтветитьУдалить
  41. Недавно столкнулся с проблемой, которая может возникнуть, если в класс, который передается в вызывающую функцию по ссылке, добавить оператор преобразования вида operator T& (). Пусть он ничего не делает и возвращает *this. Если класс Test содержит такой оператор, то GCC компилирует вызов функции

    Test someFunc()
    {
    return Test();
    }

    {
    printf("Begin\r\n");
    const Test& test = someFunc();
    printf("End\r\n");
    }

    следующим образом (выводятся диагностические сообщения) :

    Begin
    Default constructor
    operator Test&
    Copy constructor
    Destructor
    operator Test&
    Destructor
    End

    Visual C++ компилирует это иначе и программа выведет :

    Begin
    Default constructor
    End
    Destructor

    Если отбросить лишние вызовы конструкторов копий / деструкторов (спишем это на отсутствие RVO в gcc), то остается главное отличие :
    в Visual C++ переменная жива до выхода ссылки из области видимости, а GCC уничтожает её сразу.

    Пытался поискать описание такой ситуации в Стандарте 98, но, к сожалению, не нашел.

    Так какому же из компиляторов верить ?

    Лично мне кажется, что GCC себя ведет здесь неверно, поскольку в данном случае user-defined оператор T& ничем не отличается от генерируемого по умолчанию, но семантика вызова после добавления этого оператора преобразования коренным образом меняется.

    ОтветитьУдалить
  42. 2 elwood

    Во-первых, здесь мы видим, что gcc не смог выполнить Return Value Optimization, а VC смог.

    Во-вторых, это действительно интересный вопрос: если в классе определён пользовательский оператор тривиального приведения типа - нужно ли пользоваться им (gcc), или забить (VC), или выдать ошибку?

    Нужно будет на RSDN спросить. И в стандарте порыться.

    ОтветитьУдалить
  43. Comeau, кстати, выдал варнинг о том, что тривиальный пользовательский оператор никогда не будет вызван.
    Так что он здесь солидарен с VC.
    Почему же gcc пошёл по другому пути - это загадка.

    ОтветитьУдалить
  44. Во, кстати, на RSDN дискуссия трёхлетней давности про оператор приведения к самому себе:
    http://rsdn.ru/forum/cpp/1865794.aspx

    Прав комо, и причину он указывает в предупреждении:

    "ComeauTest.c", line 4: warning: "self::operator self &()" will not be called for
    implicit or explicit conversions
    operator self & () { return (*this); }

    Причина:

    12.3.2/1
    ...
    A conversion function is never used to convert a (possibly cv-qualified)
    object to the (possibly cv-qualified) same object type (or a reference to it),
    ...

    ОтветитьУдалить
  45. Обидно, что при этом

    const B& b = foo().b();

    всё же не эквивалентно

    const A& a = foo();
    const B& a = a.b();

    ОтветитьУдалить
  46. А правильно ли делать вот так:

    const Obj &myFunc()
    {
    Obj x;
    return x;
    }

    ? То есть, это функция, возвращающая константную ссылку на временный объект.

    ОтветитьУдалить
  47. al.zatv

    А правильно ли делать вот так:

    const Obj &myFunc()
    {
    Obj x;
    return x;
    }

    ? То есть, это функция, возвращающая константную ссылку на временный объект.


    Зависит от того что понимать под "правильно". Синтаксис валидный.
    Но нет гарантий, что пользователь будет создавать ссылку на стеке, компилятор на такой код выдаст предупреждение. Во всяких разных книгах по С++ не раз говорится "никогда не возвращайте ссылку на временный объект".

    Да и приведенный в статье путь трудно назвать "правильным". Это интересный с теоретической точки зрения хак, который я бы не стала использовать в промышленном коде.

    ОтветитьУдалить
  48. Так неважно же, где пользователь сохранит ссылку. После выхода из функции ссылка на локальный объект инвалидируется. И в чём здесь хак? В том, чтобы получить ссылку на свежеосвобождённый участок стека?
    Для стрельбы в ногу и низкоуровневой отладки, разве что.

    ОтветитьУдалить
  49. Анонимный8/7/13 21:31

    Поясните пожалуйста как обходить следующий момет?

    class first
    {
    public:
    int i;
    public:
    first &operator=(const first &in_This)
    {
    i = in_This.i;
    return(*this);
    }
    };

    class obj
    {
    public:
    first mF;
    public:
    obj()
    {
    }
    obj(obj &inThis)
    {
    mF = inThis.getM();
    }
    ~obj()
    {
    }

    first &getM(first &out_m)
    {
    out_m = mF;
    return(out_m);
    }
    const first &getM(void)
    {
    return(mF);
    }
    obj &setM(const first &in_m)
    {
    mF = in_m;
    return(*this);
    }
    };

    int _tmain(int argc, _TCHAR* argv[])
    {
    setlocale( LC_ALL, ".1251" );

    obj o;
    obj k;
    first f;
    k.setM(o.getM());
    k.setM(o.getM(f));

    obj d(k);
    }

    Все работает, но если что логично изменим функцию так:
    obj(const obj &inThis)
    {
    mF = inThis.getM();
    }
    Все ломается.

    Спасибо...

    ОтветитьУдалить
  50. Исправить это элементарно.
    first const& obj::getM(void) const

    что логично, поскольку getM ни меняет объект, ни отдаёт на сторону права на изменение.

    ОтветитьУдалить
  51. Прокоментируйте этот код
    http://stackoverflow.com/questions/20476669/g-segmentation-fault-when-enabling-optimization
    Почему здесь ломается?

    ОтветитьУдалить
  52. На StackOverflow уже ответили.
    Expression k в конструкторе захватывает ссылку на временный объект, который разрушается сразу после завершения конструктора.
    Дальнейшее - неопределённое поведение, которое в условиях оптимизации может выглядеть вообще как угодно.

    ОтветитьУдалить