LINUX.ORG.RU

[C++] К вопросу о передаче временного объекта по НЕ-константной ссылке.

 


0

2

Есть вот такой класс и вот такая ф-я:

class A{
...
   A& self(){ return *this; }
   void close();
   ~A(){ close(); }
};
...
void f(A&);
Почти все знают что так делать нельзя (вар. 1)
f(A());
а вот так можно (вар. 2)
A a;
f(a);
a.close();
Я иногда делаю так (вар. 3)
f( A().self() );

Насколько мне помнится, вар. 1 запрещен стандартом как одна из защит от дурака (типа при передаче временного объекта по не-константной ссылке он может быть изменен а потом изменения могут быть потеряны). Тем не менее такая необходимость встречается, например сформировали поток, функция в него отписалсь, поток закрылся. В C++0x для таких вещей даже расширили синтаксис.

Сравним вар 2 и вар. 3. Вар 3 во первых втрое лаконичней, во вторых если в вар. 2 забыть a.close(), то потом в рантайме могут вылезти неприятные и весьма трудноуловимые ошибки. Опять таки, насколько мне помнится временный объект будет жить пока не завершится вызов f, поэтому ИМНО вар 3 вполне безопасен.

Очень хотелось бы услышать аргументированное мнение специалистов насколько вар 3 корректен. Под аргументированным я понимаю объяснение того, ПОЧЕМУ так делать нельзя и КОГДА так делать нельзя (конкретные примеры ошибок к которым может приводить вар 3, или например описание гипотетических ситуаций когда временный объект не доживет до окончания вызова f). Заявления типа «так делать нельзя потому, что так делать нельзя никогда» аргументированными не считаются;-)

★★★★★

Ответ на: комментарий от mi_estas

Так можно со всех точек зрения;-).

AIv ★★★★★
() автор топика
Ответ на: комментарий от mi_estas

итак, вопрос сегодняшней викторины - чем различается вызов функции от использования оператора ветвления «if»?

shty ★★★★★
()

>во вторых если в вар. 2 забыть a.close(), то потом в рантайме могут вылезти неприятные и весьма трудноуловимые ошибки

надуманная ситуация. если подразумевается вызов close в деструкторе, то он вызовется, когда a из области видимости выйдет. если a - какой то глобальный объект или долгожитель, тогда нет смысла вызывать close после вызова функции

s0L
()
Ответ на: комментарий от s0L

> надуманная ситуация.

Ни разу! Отписался в файл, и в той же области видимости попытался его прочитать.

AIv ★★★★★
() автор топика
Ответ на: комментарий от mi_estas

Футбол отупляет

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

staseg ★★★★★
()

как насчет такого синтетического примера?

class A
{
	char data[20*1024*1024];
public:
	A& self() {return *this;}
};

void foo(A& a)
{
}

int main(int argc, char* argv[])
{
    //будет работать	
    A* a = new A;
    foo(*a);
    delete a;
	
    //не будет работать
    foo(A().self());
	
    return 0;
}
s0L
()
Ответ на: комментарий от AIv

>в этом случае и вар 2 не работает -> не аргумент.

хаха, почему не агрумент? потому что не хочется признавать что я смог пример привести? не вижу никаких противоречий примера с топиком.

s0L
()
Ответ на: комментарий от s0L

>почему не агрумент?

Потому что ты тупица.

потому что не хочется признавать что я смог пример привести?

Ты б еще на ноль поделил в конструкторе.

не вижу никаких противоречий примера с топиком

В топике речь о временных объектах smth_t(), которые всегда создаются на стеке.

staseg ★★★★★
()

Склонен думать, что создатели стандарта перебздели.

pathfinder ★★★★
()
Ответ на: комментарий от s0L

хаха, почему не агрумент?

Вы в топике команды new/delete видели? Ваш пример ложится не потому вар 3 не работает, а потому что слишком толстый объект на стеке завести пытаетесь. Если просто сделать в main вот так

A a;
то тоже работать не будет, причем тут передача кого то куда то? ЧТо вообще Ваш пример доказывает, что весь C++ не работает?

У все считающих себя профессиональными программистами такие проблемы с логикой как у Вас?

AIv ★★★★★
() автор топика

>Опять таки, насколько мне помнится временный объект будет жить пока не завершится вызов f

Ну еще бы он удалился раньше.

К вопросу о передаче временного объекта по НЕ-константной ссылке.

Передача чего-либо по неконстантной ссылке — то еще уродство.

Делай как нравится, хуже не будет. Вон, в GLib спокойно сохраняют инты в указателях, нарушают strict aliasing, кастуют что попало к void*, и ничего, гном работает и не падает на пустом месте.

AST-PM-105
()

Судя по тому, что никто из гуру не отписался, явных противопоказаний против вар. 3 нет.

Всем спасибо за обсуждение, надеюсь что у s0L паркет не сильно потрескался от ударов лысины и обильных хлопьев пены;-)

AIv ★★★★★
() автор топика

Это конечно лишь способ обмана компилятора, но если уж делать такое, то я бы предпочёл не уродовать класс, а сделать так.

template <typename T>
T& constCast (const T &a)
{
    return const_cast<T&>(a);
}

foo(constCast(A()));
Booster ★★
()
Ответ на: комментарий от AIv

Кстати, лично я считаю, что это вполне нормальная операция. Не даром в C++0x ввели rvalue references, которые абсолютно законно модифицировать. Данный случай как раз временный объект - rvalue references. Даже затрудняюсь сказать почему gcc считает это ахтунгом.

Booster ★★
()
Ответ на: комментарий от AIv

Из-за того что человек хочет модифицировать временный объект, это ещё не повод его называть дураком. ^)

Booster ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.