Russian Qt Forum

Программирование => С/C++ => Тема начата: vizir.vs от Декабрь 05, 2014, 12:53



Название: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 12:53
Доброго дня.
Есть следующий код:
Код:
#include <iostream>

class A
{
public:
A() {a = new int(5);
std::cout << "const " << a << std::endl;}
int* getA() const {return a;}
~A(){delete a;}

private:
A(const A&){}
int* a;
};

void f(const A& a)
{
std::cout << *a.getA() << std::endl;
}

int main()
{
f(A());
return 0;
}

MinGW утверждает что в f(A()) ему понадобиться конструктор копирования (!!!) и выдает ошибку компиляции. Почему? Эксперименты показали, что на деле конструктор копирования вызывать он не будет (Это можно глянуть, если конструктор копирования вынести в public и добавить вывод в него).

В общем я теряюсь в догадках, почему так.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 12:58
А если так:
Код
C++ (Qt)
A a;
f(a);
 
?


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Пантер от Декабрь 05, 2014, 12:58
Да, A(const A&){} тут реализация не нужна, то есть, A(const A&);
И раз запрещаешь копирующий конструктор, то запрещай и оператор присваивания.


Название: Re: Передача анонимных объектов по константн
Отправлено: Igors от Декабрь 05, 2014, 13:16
MSVC это пропускает, а в др компиляторах то же самое - конструктор копирования не должен быть private. Наверное дело в том что константная ссылка разрешает копирование (неважно будет ли оно на самом деле)


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:03
MSVC это пропускает, а в др компиляторах то же самое - конструктор копирования не должен быть private. Наверное дело в том что константная ссылка разрешает копирование (неважно будет ли оно на самом деле)
С каких это пор он не должен быть private??? Посмотри Кьютишные реализации - там во всю юзается макрос Q_DISABLE_COPY. И константной ссылке не нужно копирование.


Название: Re: Передача анонимных объектов по константн
Отправлено: kuzulis от Декабрь 05, 2014, 14:06
Цитировать
MSVC это пропускает

Оно много чего пропускает.. Вот, недавно хотел перевести некий проект на MinGW - а оно ппц.. не дает.. Там столько кода говна что разгребать и разгребать.. И да - студия расслабляет. :)


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:14
А если так:
Код
C++ (Qt)
A a;
f(a);
 
?
Так работает. Все компилиться.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:15
А что за компиль у тебя?


Название: Re: Передача анонимных объектов по констант&#
Отправлено: vizir.vs от Декабрь 05, 2014, 14:16
Да, A(const A&){} тут реализация не нужна, то есть, A(const A&);
И раз запрещаешь копирующий конструктор, то запрещай и оператор присваивания.
Это да, оператор тоже надо запрещать. Я обычно использую boost::noncopyable. В данном случае оператор присваивания не играл ни какой роли.


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:17
А что за компиль у тебя?
c:\test>c++ -v
Reading specs from C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:18
Ого, какое гавно мамонта. А почему на 4 не перейдешь?


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:19
MSVC это пропускает, а в др компиляторах то же самое - конструктор копирования не должен быть private. Наверное дело в том что константная ссылка разрешает копирование (неважно будет ли оно на самом деле)
Конструктор копирования должен быть private. Вопрос, какого фига компилятор (скорее семантический анализатор) требует конструктор копирования, если он его вообще не использует.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:21
Скорее всего, тут A() воспринимается как временный объект и делается попытка его скопировать, чтобы отдать корректную ссылку. Может, бага компилятора?


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:23
Ого, какое гавно мамонта. А почему на 4 не перейдешь?
В свое время скачал Qt4.8.1 и там этот мингв был в поставке + собран под него. Менять в падлу, это возможно еще и boost перекомпилять, и еще пара библиотек. Ну его нафиг. К тому же под винду я очень редко пишу, в основном все под unix системы, а винда так...


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:25
Скорее всего, тут A() воспринимается как временный объект и делается попытка его скопировать, чтобы отдать корректную ссылку. Может, бага компилятора?
В том то и дело, что если добавить вывод в конструктор копирования или в конструктор, то можно увидеть, что копирования не происходит. К тому же я понял бы, если бы требовался конструктор копирования для не константной ссылки. Для константной ссылки время жизни временного объекта удлиняется и поэтому копирования вообще не имеет смысла.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:27
Цитировать
Для константной ссылки время жизни временного объекта удлиняется
На сколько я помню, это появилось только в 11 стандарте, что временный объект живет пока жива константная ссылка на него. До 11 стандарта это было сугубо поведение в конкретных компиляторах. Поищи статью Майерса по этой теме.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: vizir.vs от Декабрь 05, 2014, 14:30
Цитировать
Для константной ссылки время жизни временного объекта удлиняется
На сколько я помню, это появилось только в 11 стандарте, что временный объект живет пока жива константная ссылка на него. До 11 стандарта это было сугубо поведение в конкретных компиляторах. Поищи статью Майерса по этой теме.
На сколько я помню, это было еще до 11 стандарта. Нашел http://alenacpp.blogspot.ru/2008/01/const.html. Статья от 2008 года. 11 стандарта тогда еще не было.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:31
Тьфу, Саттер http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 14:32
1. Это не было описано в стандарте.
2. У тебя еще важная вещь - твой объект неименованный.


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 14:35
1. Это не было описано в стандарте.
2. У тебя еще важная вещь - твой объект неименованный.
Но все равно, получается, что компилятор требует конструктор копирования, но при этом его не использует. Даже если объект уничтожиться в процессе выполнения функции, ему какое дело, что у меня нет конструктора копирования? Он же копировать не должен, это ссылка. Ссылки не копируют объект.


Название: Re: Передача анонимных объектов по константн
Отправлено: Igors от Декабрь 05, 2014, 16:02
С каких это пор он не должен быть private??? Посмотри Кьютишные реализации - там во всю юзается макрос Q_DISABLE_COPY. И константной ссылке не нужно копирование.
То же будет и с Qt классами, напр
Код
C++ (Qt)
void Test( const QWidget & w )
{
}
 
int main( int argc, char *argv[] )
{
Test(QPushButton("btn"));
return 0;
}
 
Цитировать
.../Headers/qpushbutton.h:116: error: 'QPushButton::QPushButton(const QPushButton&)' is private
Причем на многих компиляторах: gcc. icc, clang

Я это понимаю так
Код
C++ (Qt)
void Test( const QString & txt );
 
// 1
QString txt("abc");
Test(txt);
 
// 2
Test("abc");
 
В первом случае объект txt существует, он и используется, фактически подается его адрес. Во втором создается временный объект, QString должен иметь конструктор принимающий const char *

Возвращаясь к теме: по каким-то причинам "самостоятельно созданный временный объект" не считается "тем на что можно сослаться" и значит его надо копировать (случай 2). Хотя это и противоречит здравому смыслу  :)
Цитировать
Больной, проснитесь и примите снотворное!
:)


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 16:06
Igors, дабы не быть голословным, приведи, пожалуйста, версии компиляторов.


Название: Re: Передача анонимных объектов по константн
Отправлено: Пантер от Декабрь 05, 2014, 16:09
Заюзал clang, вот что получил
Код:
main.cpp:9:7: warning: C++98 requires an accessible copy constructor for
      class 'QPushButton' when binding a reference to a temporary; was
      private [-Wbind-to-temporary-copy]
        Test(QPushButton("btn"));


Как я и писал в самом начале, до 11 стандарта данное поведение было отдано на откуп компиляторам.


Название: Re: Передача анонимных объектов по константн
Отправлено: Igors от Декабрь 05, 2014, 16:16
Igors, дабы не быть голословным, приведи, пожалуйста, версии компиляторов.
gcc 4.2 apple (совсем старый), icc 13.0 (где-то года 2 ему), clang помню kambala проверял с год назад, так что вполне новый


Название: Re: Передача анонимных объектов по константн
Отправлено: Igors от Декабрь 05, 2014, 16:19
Для константной ссылки время жизни временного объекта удлиняется и поэтому копирования вообще не имеет смысла.
Не понял что куда удлиняется? Деструктор временного объекта вызовется когда ф-ция (куда его подали) вернет управление. Разве в С++ 11 что-то изменилось?


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Пантер от Декабрь 05, 2014, 16:21
Для константной ссылки время жизни временного объекта удлиняется и поэтому копирования вообще не имеет смысла.
Не понял что куда удлиняется? Деструктор временного объекта вызовется когда ф-ция (куда его подали) вернет управление. Разве в С++ 11 что-то изменилось?
Пройди по ссылке выше в топике, там Саттер на счет этого пишет.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: vizir.vs от Декабрь 05, 2014, 16:46
Больше всего меня смущает, что копирования не происходит. Он его требует и примерно можно предположить почему. Но не использует. Зачем требовать то, что потом не используешь?!


Название: Re: Передача анонимных объектов по константн
Отправлено: vizir.vs от Декабрь 05, 2014, 16:49
С каких это пор он не должен быть private??? Посмотри Кьютишные реализации - там во всю юзается макрос Q_DISABLE_COPY. И константной ссылке не нужно копирование.
То же будет и с Qt классами, напр
Код
C++ (Qt)
void Test( const QWidget & w )
{
}
 
int main( int argc, char *argv[] )
{
Test(QPushButton("btn"));
return 0;
}
 
Цитировать
.../Headers/qpushbutton.h:116: error: 'QPushButton::QPushButton(const QPushButton&)' is private
Причем на многих компиляторах: gcc. icc, clang

Я это понимаю так
Код
C++ (Qt)
void Test( const QString & txt );
 
// 1
QString txt("abc");
Test(txt);
 
// 2
Test("abc");
 
В первом случае объект txt существует, он и используется, фактически подается его адрес. Во втором создается временный объект, QString должен иметь конструктор принимающий const char *

Возвращаясь к теме: по каким-то причинам "самостоятельно созданный временный объект" не считается "тем на что можно сослаться" и значит его надо копировать (случай 2). Хотя это и противоречит здравому смыслу  :)
Цитировать
Больной, проснитесь и примите снотворное!
:)

В моем примере mingw требует конструктор копирования, а gcc нет. Код одинаковый, поведение разное.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Igors от Декабрь 05, 2014, 17:06
Больше всего меня смущает, что копирования не происходит. Он его требует и примерно можно предположить почему. Но не использует. Зачем требовать то, что потом не используешь?!
А почему Вас это удивляет? Случай совсем не редкий, напр
Код
C++ (Qt)
MyClass GetA( void );
..
MyClass a = GetA();
Как Вы знаете здесь никакого копирования нет, вызывается 1 конструктор (извращения MSVC не в счет). Тем не менее также требуется конструктор копирования


Название: Re: Передача анонимных объектов по константн
Отправлено: _Bers от Декабрь 06, 2014, 00:27
1. Это не было описано в стандарте.
2. У тебя еще важная вещь - твой объект неименованный.

старый стандарт ковырять лень.
однако совершенно точно знаю, что с++03 msvc/mingw/gcc продлевали жизнь временного объекта посредством константной ссылки

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



Название: Re: Передача анонимных объектов по констант&#
Отправлено: _Bers от Декабрь 06, 2014, 00:39
Код
C++ (Qt)
MyClass GetA( void );
..
MyClass a = GetA();
Как Вы знаете здесь никакого копирования нет, вызывается 1 конструктор (извращения MSVC не в счет). Тем не менее также требуется конструктор копирования

Здесь есть копирование.
Однако срабатывает стандартная оптимизация RVO, и копирующий конструктор становится не наблюдаем.

Современные компиляторы (в том числе msvc) применяют её даже для режима "без оптимизации".

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

Примечание: msvc (по крайней мере 2008 компилятор точно, новые не проверял) допускает отсутствие копирующего конструктора, когда заранее знает, что оптимизирует его. Но это - расширение от компилятора.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Igors от Декабрь 06, 2014, 10:01
Здесь есть копирование.
Однако срабатывает стандартная оптимизация NRVO, и копирующий конструктор становится не наблюдаем.
Что значит "не наблюдаем"?  :) Он просто не вызывается.

Современные компиляторы (в том числе msvc) применяют её даже для режима "без оптимизации".
MSVC 2012 в режиме Debug вызывает конструктор копирования (в этом году проверял)


Название: Re: Передача анонимных объектов по констант&#
Отправлено: _Bers от Декабрь 07, 2014, 08:16
Что значит "не наблюдаем"?  :) Он просто не вызывается.

Он оптимизирован.

Компилятор догадывается, что результат выражения
Код:
auto obj1 = foo(); //return some(param);

Будет аналогичен выражению:

Код:
some obj1(param);

Выпиливает создание временного объекта.
А для результирующего объекта заменяет вызов копирующего конструктора на explicit ctor

При этом, допустим мы почему то захотели что бы в копирующем конструкторе были эффекты. Например запрос в БД.
А в конструкторе с параметрами таких эффектов нет.

В этом случае получается, что компилятор выполняя такую оптимизацию нарушает ожидаемую нами логику: мы рассчитывали на запрос в БД, но после оптимизации его не случилось.

По стандарту компилятор не имеет право применять оптимизации, которые изменяют "фактическое" состояние программы настолько, что оно перестает соответствовать "логическому" (такому, которое изначально было описано в исходном коде).

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

Раньше компиляторы умели RVO/NRVO, но в большинстве случаев они оказывались недопустимыми из-за возможных эффектов.
Поэтому, стандарт их узаконил.

С этого момента, компиляторы были обязаны поддерживать и использовать оптимизации RVO/NRVO.
в частности, выражение:

Код:
auto obj = some();
компилятор обязан оптимизировать до:
Код:
some obj;

Код:
auto obj = some(param);
компилятор обязан оптимизировать до:
Код:
some obj(param);


Для копирующих конструкторов была сделала оговорка.
Я точно не помню как она звучит в оригинале.

Я запомнил её в своём весьма вольном переводе:
нельзя закладываться на то, что копирующий конструктор будет наблюдаемый.

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



Поэтому эффекты в конструкторе копирования - это ill-formed.
В хорошей программе они не допустимы.




(хотя функции с эффектами, это строго говоря само по себе ill-formed.
Ответ на вопрос "почему?" можно найти в книжках аля "Идеальный код" Макконелла)


MSVC 2012 в режиме Debug вызывает конструктор копирования (в этом году проверял)


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

гцц
http://rextester.com/XUAMR22459

шланг
http://rextester.com/IOSR10427

2013 студия, ноябрь
http://rextester.com/BCN9840

2012 к сожалению не могу продемонстрировать на онлайн компиляторе.
Однако результат точно такой же. Я лично долбался с ним, пытаясь найти способ, как заставить компилятор не оптимизировать. Не нашел.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Igors от Декабрь 07, 2014, 10:01
msvc2012, тулсет: штатный из коробки, а так же ноябрьский. В дебаге оптимизирует копирование временных объектов.
msvc2013, тулсет: штатный из коробки, а так же ноябрьский. В дебаге оптимизирует копирование временных объектов.
Да бог с ним, в MSVC может быть что угодно. Проблема в том что "нормальные" компиляторы требуют конструктор копирования, хотя и не используют его. Это вызывает неудобства если класс планируется как неперемещаемый. Можно ли сказать что в С++ 11 эта проблема решена?

Спасибо


Название: Re: Передача анонимных объектов по констант&#
Отправлено: _Bers от Декабрь 07, 2014, 14:43
Можно ли сказать что в С++ 11 эта проблема решена?

с++03 узаконили RVO/NRVO
c++11 вытащили контроль за r-value на уровень пользователя.

Однако принципиально ничего не изменилось.

Я предоставил ссылки выше, которые иллюстрируют, что r-value конструкторы так же не наблюдаемы.


Название: Re: Передача анонимных объектов по констант&#
Отправлено: _Bers от Декабрь 07, 2014, 14:47
Да бог с ним, в MSVC может быть что угодно.

Совершенно напрасно так думаете. msvc2012 полностью соответствует стандарту.

Он позволяет по дефолту собственные расширения, которые значительно облегчают жизнь для не_кросс_платформы.

Но при этом, если код соответствует стандарту - msvc его скомплиирует, и поведение будет ожидаемым. Инфа 100%


Название: Re: Передача анонимных объектов по констант&#
Отправлено: Igors от Декабрь 08, 2014, 12:02
Совершенно напрасно так думаете. msvc2012 полностью соответствует стандарту.
Не "думаю", а "на горьком опыте" :) Ладно, не хочу тратить Ваше и свое время на "уличение MSVC", лучше вот что спрошу:

Есть класс копирование которого нежелательно. Напр у меня контейнер возможно содержащий гигабайты данных. Хорошо, делаем конструктор и оператор = private. Но надо распараллелить расчеты, поэтому каждая нитка должна иметь свой такой контейнер. Приходится отказаться от private. Конечно всегда можно делать указатель, но это не очень удобно. Как грамотно разрулить эту ситуацию?

Спасибо


Название: Re: Передача анонимных объектов по константн
Отправлено: Old от Декабрь 08, 2014, 12:12
Реализовать этот контейнер с implicit shared.