Передача временных переменных по ссылке
От: slava_phirsov Россия  
Дата: 16.09.09 11:28
Оценка:
"Возможно, я простой провинциальный адвокат и ничего не смыслю в суперзлодеях" ("Братья Вентура")
Доброго времени суток читающим!
Возник вот такой вопрос: я создаю временный экземпляр класса и передаю его в качестве аргумента по ссылке. Вот примитивный тестовы пример:

#include <iostream>

using std::cout;
using std::endl;

class Foo {
    public:
        int m_data;
        
        Foo(const int& data):
            m_data(data) {};
    
    private:
        Foo(const Foo& foo);
};

void proc_foo(const Foo& foo) {
    cout << "Data: " << foo.m_data << endl;
};

int main(int argc, char* argv[]) {
    proc_foo(Foo(5));
    return 0;
};


Компилятор GCC 4.1.2 ругается:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:13: error: ‘Foo::Foo(const Foo&)’ is private
main.cpp:21: error: within this context


Насколько я понял, он не хочет передавать по ссылке созданную при вызове конструктора временную переменную, а хочет передать ее копию. Это по стандарту так, или косяки самого компилятора? Или я чего-то не учел?
Заранее спасибо.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: Передача временных переменных по ссылке
От: SolVolkov  
Дата: 16.09.09 12:29
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Компилятор GCC 4.1.2 ругается:


_>

_>main.cpp: In function ‘int main(int, char**)’:
_>main.cpp:13: error: ‘Foo::Foo(const Foo&)’ is private
_>main.cpp:21: error: within this context


_>Насколько я понял, он не хочет передавать по ссылке созданную при вызове конструктора временную переменную, а хочет передать ее копию. Это по стандарту так, или косяки самого компилятора? Или я чего-то не учел?

_>Заранее спасибо.

Обновись до 4.3
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25950
Re: Передача временных переменных по ссылке
От: GarikTot  
Дата: 16.09.09 12:52
Оценка: 2 (1)
Здравствуйте, slava_phirsov, Вы писали:

_>    proc_foo(Foo(5));

Это copy-initialization(8.5 п.12), что эквивалентно такой записи:
  const Foo& foo = Foo(5);

В текущей версии стандарта такое требует конструктора копирования, даже если копирования не произойдёт
(8.5.3 п.5):

The constructor that would be used to make the copy shall be callable whether or not the copy is
actually done. [Example:

struct A { };
struct B : public A { } b;
extern B f();
const A& rca = f(); // Either bound to the A sub-object of the B rvalue,
// or the entire B object is copied and the reference
// is bound to the A sub-object of the copy

—end example]


В c++0x вроде как хотят отменить это требование
Re: Передача временных переменных по ссылке
От: _Hooter Россия  
Дата: 19.09.09 11:57
Оценка: +1
_>Насколько я понял, он не хочет передавать по ссылке созданную при вызове конструктора временную переменную, а хочет передать ее копию. Это по стандарту так, или косяки самого компилятора? Или я чего-то не учел?

Проблема не в передаче аргумента в функцию, а в создании промежуточного объекта. Для создания промежуточного объекта действительно нужен конструктор копирования.

Такая проблема решается следующим образом:

const Foo f(5);
proc_foo(f);


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

На самом деле такая запись даже более предпочтительна, потому что более читабельна и более безопасна.
Re[2]: Передача временных переменных по ссылке
От: Николай Ивченков  
Дата: 19.09.09 13:26
Оценка: +2
Hooter:

_H>На самом деле такая запись даже более предпочтительна, потому что более читабельна и более безопасна.


Это вряд ли. Да и не везде такое применишь (например, в списке инициализации конструктора и в аргументе по умолчанию негде объявлять переменные).
Re[2]: Передача временных переменных по ссылке
От: igna Россия  
Дата: 20.09.09 08:06
Оценка:
Здравствуйте, _Hooter, Вы писали:

_H>На самом деле такая запись даже более предпочтительна, потому что более читабельна и более безопасна.


Чем же она более безопасна?
Re[3]: Передача временных переменных по ссылке
От: Artom Голландия  
Дата: 21.09.09 09:57
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Hooter:


_H>>На самом деле такая запись даже более предпочтительна, потому что более читабельна и более безопасна.


НИ>Это вряд ли. Да и не везде такое применишь (например, в списке инициализации конструктора и в аргументе по умолчанию негде объявлять переменные).


Значит лучше что бы копи-конструктор создавал объект? С глаз долой и из сердца вон!
Только если он явно не определён (что очень часто встречается), то во-первых, скорее всего объект создаётся где придётся, i.e. на стеке. Соответственно, если число таких неявных созданий растёт (ведь в вашем примере с инициализацией в списке конструктора придётся незабыть и про удаление в деструкторе), имеем проблемы перерполнения стека. А во-вторых,что бы найти такой трик в проге — надо изрядно попотеть, так как понять такую "неявность" сначало трудно — подСознание обманчиво подсказывет, что надо искать место, где объект создаётся...и начинается долгий путь к великому замыслу уволевшегося коллеги...
Думаю Hooter прав и насчёт читабельности.

Кстати, кроме приведённых Вами примеров, ситуации, где такое неявное создание необходимо, в голову не приходят. И тем более, ситуации, где по другому нельзя. Может с архитектурой что-то не так, раз понадобились такие пути? Это IMHO, поэтому, просвятите, плз.
Re[2]: Передача временных переменных по ссылке
От: GarikTot  
Дата: 21.09.09 10:45
Оценка: 2 (1)
Здравствуйте, _Hooter, Вы писали:

_H>Проблема не в передаче аргумента в функцию, а в создании промежуточного объекта. Для создания промежуточного объекта действительно нужен конструктор копирования.

В этом случае объект создаётся всего один (копирование не выполняется), ктр. живет до выхода из функции. Такое поведение, насколько я понимаю, соответствует ожиданиям автора исходного сообщения.
Другое дело, что компилятор (в соответствии с требованием стандарта) требует открытого конструктора копирования. Что, на мой взгляд, не совсем логично. В c++0x это требование убрали. Те компиляторы (как уже говорили выше), ктр. поддерживают фичи нового стандарта, прекрасно компилируют такой код
Re[3]: Передача временных переменных по ссылке
От: Юрий Жмеренецкий ICQ 380412032
Дата: 21.09.09 12:41
Оценка: 1 (1)
Здравствуйте, igna, Вы писали:

I>Здравствуйте, _Hooter, Вы писали:


_H>>На самом деле такая запись даже более предпочтительна, потому что более читабельна и более безопасна.


I>Чем же она более безопасна?


В специфических контекстах такое использование (отдельный объект) м. б. безопасней с точки зрения потенциальных утечек, см. например boost::ptr_multimap_adapter:

// При использовании 'const key_type& k' в качестве первого параметра 
// гарантию отсутствия утечек (strong exception guarantee) можно 
// обеспечить только с использованием отдельного объекта при вызове.
iterator  insert( key_type& k, T* x ); 

template< class U >
iterator  insert( const key_type&, std::auto_ptr<U> x1 );


Обе функции передают владение указателем контейнеру. Первый вариант фактически принуждает к созданию отдельного объекта 'key_type', иначе (с использование 'const key_type& k') при возбуждении исключения в конструкторе 'key_type' при создании временного объекта, указатель 'x' может утечь. Потенциальная утечка связана с тем, что порядок вычисления арументов неопределен и сначала может произойти вычисление второго аргумента (например 'new T(args)'), а при вычислении второго может возникнуть исключение. Для исключения этой ситуации квалификатор 'const' отсутствует в первой функции, и поэтому вычисление первого аргумента никогда не заканчивается исключением.
Re[4]: Передача временных переменных по ссылке
От: Николай Ивченков  
Дата: 21.09.09 21:11
Оценка:
Artom:

A>Значит лучше что бы копи-конструктор создавал объект?


Как много реализаций создают-таки копию при инициализации ссылки? =) В будущем стандарте это будет вообще под запретом. Ну, да, есть маленький шанс столкнуться с экзотическим компилятором и получить overhead. Но при чём тут читаемость и безопасность? Читаемость-то как раз лучше с temporary objects, т.к. для читателя их время жизни и использование максимально локализованы. Определение же переменной зрительно отделено от вызова функции, и переменная потенциально может быть использована в нескольких местах.

И, кстати, если в некоей f передаваемый аргументом объект дальше где-то сохраняется, то в дальнейшем к f(X const &) можно будет добавить f(X &&), где копирование может быть заменено перемещением. К переменной же f(X &&) применяться не будет.

A>Только если он явно не определён (что очень часто встречается), то во-первых, скорее всего объект создаётся где придётся, i.e. на стеке. Соответственно, если число таких неявных созданий растёт (ведь в вашем примере с инициализацией в списке конструктора придётся незабыть и про удаление в деструкторе), имеем проблемы перерполнения стека.


Это сколько ж надо насоздавать объектов, чтобы переполнение стека получить?

A>Кстати, кроме приведённых Вами примеров, ситуации, где такое неявное создание необходимо, в голову не приходят.


while (f(T(x)))
{
    .... // здесь x меняется
}


A>Может с архитектурой что-то не так, раз понадобились такие пути?


Ну, конечно, как чуть что, так сразу с архитектурой что-то не так. И откуда только такие умные архитекторы берутся? В некоторых (если не в большинстве) случаях(-ев) две архитектуры вообще невозможно сравнить на предмет "лучше"/"хуже" в целом. Подобными сравнениями обычно занимаются пиарщики или флеймеры-холиварщики, я же в этом предпочитаю не участвовать (за редкими исключениями).
Re[5]: Передача временных переменных по ссылке
От: Artom Голландия  
Дата: 22.09.09 09:48
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Artom:


A>>Значит лучше что бы копи-конструктор создавал объект?


НИ>Как много реализаций создают-таки копию при инициализации ссылки? =)В будущем стандарте это будет вообще под запретом.

Что именно имеется ввиду? У меня, видимо, отличные крепкие ассоциации со словом "Реализация"...

НИ>Ну, да, есть маленький шанс столкнуться с экзотическим компилятором и получить overhead. Но при чём тут читаемость и безопасность? Читаемость-то как раз лучше с temporary objects, т.к. для читателя их время жизни и использование максимально локализованы.

Согл., для тех, кто предупреждён — да!

НИ>Определение же переменной зрительно отделено от вызова функции, и переменная потенциально может быть использована в нескольких местах.

Конечно тут я соглашусь, НО мы же говорили не об определении переменных и не о простом их использовании! Речь шла о создании временных объектов, не так ли? Или мы говорим о разных вещах? Тогда — пардон.

НИ>И, кстати, если в некоей f передаваемый аргументом объект дальше где-то сохраняется, то в дальнейшем к f(X const &) можно будет добавить f(X &&), где копирование может быть заменено перемещением. К переменной же f(X &&) применяться не будет.

А по-моему, это только усугубит проблему поиска места истинного создания...

A>>Только если он явно не определён (что очень часто встречается), то во-первых, скорее всего объект создаётся где придётся, i.e. на стеке. Соответственно, если число таких неявных созданий растёт (ведь в вашем примере с инициализацией в списке конструктора придётся незабыть и про удаление в деструкторе), имеем проблемы перерполнения стека.

НИ>Это сколько ж надо насоздавать объектов, чтобы переполнение стека получить?
Случай из практики...Например графика. Допустим, объект в данном случае был Mesh или Poligone! Воображеие рисует уже?

A>>Кстати, кроме приведённых Вами примеров, ситуации, где такое неявное создание необходимо, в голову не приходят.

НИ>
while (f(T(x)))
НИ>{
НИ>    .... // здесь x меняется
НИ>}

Да я же не имею в виду код создания! Это уже автор топика показал — зачем же дублировать. Я имею в виде именно реальную архитектуру приложения, где такое неявное создание просто незаменимо! И не для понта спрашиваю, а потому что лично такого не встречалось. Обычно было так, что объекты со сложными конструкторами возникали на старейших фазах проекта, когда надо было какие-то проблемы ранней архитектуры залатать. Так же относительно следующего:

A>>Может с архитектурой что-то не так, раз понадобились такие пути?

НИ>Ну, конечно, как чуть что, так сразу с архитектурой что-то не так. И откуда только такие умные архитекторы берутся? В некоторых (если не в большинстве) случаях(-ев) две архитектуры вообще невозможно сравнить на предмет "лучше"/"хуже" в целом. Подобными сравнениями обычно занимаются пиарщики или флеймеры-холиварщики, я же в этом предпочитаю не участвовать (за редкими исключениями).
Жаль что до сих пор так считаете.
Re[6]: Передача временных переменных по ссылке
От: Николай Ивченков  
Дата: 22.09.09 13:13
Оценка:
Artom:

НИ>>Как много реализаций создают-таки копию при инициализации ссылки? =)В будущем стандарте это будет вообще под запретом.

A>Что именно имеется ввиду?

Конкретные компиляторы.

НИ>>Ну, да, есть маленький шанс столкнуться с экзотическим компилятором и получить overhead. Но при чём тут читаемость и безопасность? Читаемость-то как раз лучше с temporary objects, т.к. для читателя их время жизни и использование максимально локализованы.

A>Согл., для тех, кто предупреждён — да!

О чём предупреждён?

НИ>>Определение же переменной зрительно отделено от вызова функции, и переменная потенциально может быть использована в нескольких местах.

A>Конечно тут я соглашусь, НО мы же говорили не об определении переменных и не о простом их использовании! Речь шла о создании временных объектов, не так ли?

В терминологии стандарта

const Foo f(5);

это именно определение переменной, где f — переменная, а не временный объект.

НИ>>И, кстати, если в некоей f передаваемый аргументом объект дальше где-то сохраняется, то в дальнейшем к f(X const &) можно будет добавить f(X &&), где копирование может быть заменено перемещением. К переменной же f(X &&) применяться не будет.

A>А по-моему, это только усугубит проблему поиска места истинного создания...

Какие-то странные у тебя проблемы. Здесь речь, в общем-то, идёт об обычной оптимизации, достигаемой за счёт семантики перемещения.

НИ>>Это сколько ж надо насоздавать объектов, чтобы переполнение стека получить?

A> Случай из практики...Например графика. Допустим, объект в данном случае был Mesh или Poligone! Воображеие рисует уже?

И что — все данные тяжёлого объекта ты хранишь в автоматически распределяемой памяти? Ну, тогда ты ССЗБ.

A>Я имею в виде именно реальную архитектуру приложения, где такое неявное создание просто незаменимо!


А кому нужны именно _незаменимые_ решения? Если решение поставленной задачи можно заменить на другое, то что тогда?
Re[7]: Передача временных переменных по ссылке
От: Artom Голландия  
Дата: 23.09.09 15:18
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Artom:

НИ>>>Определение же переменной зрительно отделено от вызова функции, и переменная потенциально может быть использована в нескольких местах.
A>>Конечно тут я соглашусь, НО мы же говорили не об определении переменных и не о простом их использовании! Речь шла о создании временных объектов, не так ли?

НИ>В терминологии стандарта

НИ>
const Foo f(5);

НИ>это именно определение переменной, где f — переменная, а не временный объект.
смотря что вы называете определене переменной: initialization или assignment. Если ещё более конкретнее, то в терм. станд. это инициализация переменной. но в данм примере это может быть и присваивание. Давайте определимся с терминами, плз.


НИ>>>И, кстати, если в некоей f передаваемый аргументом объект дальше где-то сохраняется, то в дальнейшем к f(X const &) можно будет добавить f(X &&), где копирование может быть заменено перемещением. К переменной же f(X &&) применяться не будет.

A>>А по-моему, это только усугубит проблему поиска места истинного создания...
НИ>Какие-то странные у тебя проблемы. Здесь речь, в общем-то, идёт об обычной оптимизации, достигаемой за счёт семантики перемещения.
Что такое семантика перемещения? Плз! Представляется некий теоретический монстр, сорри!


НИ>>>Это сколько ж надо насоздавать объектов, чтобы переполнение стека получить? A>> Случай из практики...Например графика. Допустим, объект в данном случае был Mesh или Poligone! Воображеие рисует уже?

НИ>И что — все данные тяжёлого объекта ты хранишь в автоматически распределяемой памяти? Ну, тогда ты ССЗБ.
Что по вашему есть авто распред. памяти? Впервые слышу термин. Плз., поясните! Может вы имеете ввиду диамически-распред. память. Тогда да — я распределяю память динамически для как можно большего количества объектов. А что, по вашему я должен ложить все на стек?
Что такое ССЗБ? Надеюсь это не оскорбительное!


A>>Я имею в виде именно реальную архитектуру приложения, где такое неявное создание просто незаменимо!

НИ>А кому нужны именно _незаменимые_ решения? Если решение поставленной задачи можно заменить на другое, то что тогда?
Но зачем тогда усложнять?
Re[8]: Передача временных переменных по ссылке
От: Николай Ивченков  
Дата: 23.09.09 21:48
Оценка:
Artom:

A>смотря что вы называете определене переменной: initialization или assignment.


Definition, initialization и assignment — это три разных термина. Определением я называю definition (что, вроде как, общепринято).

A>Что такое семантика перемещения?

A>Что такое ССЗБ?

Открой для себя Google.
"семантика перемещения" С++
ССЗБ

A>Что по вашему есть авто распред. памяти?


Здесь я имел в виду память, принадлежащую непосредственно объекту с автоматическим временем жизни.

A>я распределяю память динамически для как можно большего количества объектов.


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

A>>>Я имею в виде именно реальную архитектуру приложения, где такое неявное создание просто незаменимо!

НИ>>А кому нужны именно _незаменимые_ решения? Если решение поставленной задачи можно заменить на другое, то что тогда?
A>Но зачем тогда усложнять?

Что усложнять?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.