Здравствуйте, Старостин Василий Викторович, Вы писали:
СВВ>Аннотация: СВВ>Несколько веселых и интересных примеров на языке C++.
Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Старостин Василий Викторович, Вы писали:
СВВ>>Аннотация: СВВ>>Несколько веселых и интересных примеров на языке C++.
PD>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
К молодым людям нельзя относиться свысока. Очень может быть, что повзрослев, они станут выдающимися мужами. Только тот, кто ничего не достиг, дожив до сорока или пятидесяти лет, не заслуживает уважения.
Благородный муж помогает людям увидеть доброе в себе и не поучает людей видеть в себе дурное. А низкий человек поступает наоборот.
Благородный муж знает о своем превосходстве, но избегает соперничества. Он ладит со всеми, но ни с кем не вступает в сговор.
Благородный муж в душе безмятежен. Низкий человек всегда озабочен.
J>К молодым людям нельзя относиться свысока. Очень может быть, что повзрослев, они станут выдающимися мужами. Только тот, кто ничего не достиг, дожив до сорока или пятидесяти лет, не заслуживает уважения.
Все верно, но это не причина публиковать подобные открытия молодых людей.
Howard Hinnant из Library Working Group, с которым я недавно беседовал, не видит ничего плохого в том, что у std::pair и std::tuple при инстанцировании их ссылочными типами copy/move конструкторы делают совсем не то же самое, что copy/move операторы присваивания, и вот такое необычное поведение программы его, похоже, полностью устраивает.
Здравствуйте, CreatorCray, Вы писали:
CC>Статья то ни о чём. CC>Можно сказать: выжимка "забавного" из местных форумов.
Ну... да.
Она и названа вовсе не "глубокая всеобъемлющая статья об особенностях трансляции С++".
Основная идея была написать короткую "легкую" статью, о том что зацепило в детстве. Кого не цепляло, могу только посочувствовать!
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Здравствуйте, Pavel Dvorkin, Вы писали:
J>>К молодым людям нельзя относиться свысока. Очень может быть, что повзрослев, они станут выдающимися мужами. Только тот, кто ничего не достиг, дожив до сорока или пятидесяти лет, не заслуживает уважения. PD>Все верно, но это не причина публиковать подобные открытия молодых людей.
Вы сами назвали это открытием и приписали это автору.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
int main()
{
const int i = 0;
prn(1); A a1();
prn(2); A a2 = i;
prn(3); A a3(i);
prn(4); A a4 = A(i);
prn(5); A a5 = (A) i;
prn(6); A a6 = static_cast<A>(i);
return 0;
}
В четвертом случае может вызваться конструктор копирования. Насколько помню по стандарту в таком случае разрешается как создавать временный объект, а потом копировать его, так и создавать нужный объект сразу на месте размещения.
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
PD>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
первый раз захотелось поставить тебе оценку "Спасибо"
Of course, the code must be complete enough to compile and link.
Здравствуйте, jyuyjiyuijyu, Вы писали:
СВВ>>>Аннотация: СВВ>>>Несколько веселых и интересных примеров на языке C++.
PD>>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
J>К молодым людям нельзя относиться свысока.
Ну автора можно понять. По-настоящему удивительно, почему такую статью приняли к публикации .
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Старостин Василий Викторович, Вы писали:
СВВ>>Аннотация: СВВ>>Несколько веселых и интересных примеров на языке C++.
PD>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
Здравствуйте, enji, Вы писали:
E>Пусть первым выполнится i++, тогда получим 5 + 7 = 12 E>Пусть первым выполнится ++i, тогда получим 6 + 6 = 12
ты не знаком с sequence points? операции инкременитрования и взятия значений слева и спртава от + могут выполняться в любом порядке. это позволяет генерировать наиболее оптимальную программу
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Здравствуйте, Старостин Василий Викторович, Вы писали:
PD>>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
J>К молодым людям нельзя относиться свысока. Очень может быть, что повзрослев, они станут выдающимися мужами. Только тот, кто ничего не достиг, дожив до сорока или пятидесяти лет, не заслуживает уважения.
J>- Конфуций
Надо пометку какую-нибудь ставить. Типа "для самых маленьких". Ну или не так сурово "для начинающих".
Здравствуйте, Basil2, Вы писали:
CC>>Статья то ни о чём. CC>>Можно сказать: выжимка "забавного" из местных форумов.
B>Ну... да. B>Она и названа вовсе не "глубокая всеобъемлющая статья об особенностях трансляции С++". B>Основная идея была написать короткую "легкую" статью, о том что зацепило в детстве. Кого не цепляло, могу только посочувствовать!
Тогда надо было писать в КУ
Здравствуйте, Masterkent, Вы писали:
M>Раз уж завели такой топик, покажу один из подарочков, уготованных нам комитетом по стандартизации C++:
M>http://ideone.com/CoyN7
M>Howard Hinnant из Library Working Group, с которым я недавно беседовал,
А лог беседы доступен?
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Здравствуйте, enji, Вы писали:
E>>int i = 5; E>>int j = i++ + ++i;
E>>Как получить 10, 11 и 14?
BZ>t1 = i BZ>++i BZ>t2 = i BZ>i++ BZ>j=t1+t2
BZ>вышло 11. аналогично можно получить 13. 10 и 14 — не выйдет. зато 12 можно получить 2 способами — и как 5+7, и как 6+6
Не, стоп.
j = ++i никак не эквивалентно t1=i; ++i; j=t1
С точками следования я знаком и понимаю, что ++i и i++ могут быть вычислены в любом порядке, но до операции сложения. Однако как получить 11, все равно не ясно
Здравствуйте, jyuyjiyuijyu, Вы писали:
PD>>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.
J>К молодым людям нельзя относиться свысока. Очень может быть, что повзрослев, они станут выдающимися мужами. Только тот, кто ничего не достиг, дожив до сорока или пятидесяти лет, не заслуживает уважения. J>Благородный муж помогает людям увидеть доброе в себе и не поучает людей видеть в себе дурное. А низкий человек поступает наоборот. J>Благородный муж знает о своем превосходстве, но избегает соперничества. Он ладит со всеми, но ни с кем не вступает в сговор. J>Благородный муж в душе безмятежен. Низкий человек всегда озабочен. J>- Конфуций
Вот просто интересно, что делает Благородный муж, когда не одобряет действий Молодого человека — говорит "пиши еще, Вася"?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, enji, Вы писали:
E>>>int i = 5; E>>>int j = i++ + ++i;
E>>>Как получить 10, 11 и 14?
BZ>>t1 = i BZ>>++i BZ>>t2 = i BZ>>i++ BZ>>j=t1+t2
BZ>>вышло 11. аналогично можно получить 13. 10 и 14 — не выйдет. зато 12 можно получить 2 способами — и как 5+7, и как 6+6
E>Не, стоп.
E>j = ++i никак не эквивалентно t1=i; ++i; j=t1
а ты погляди внимательней на мои примеры я там специально различаю i++ и ++i. и найди хоть одно нарушение правил следования
> Пусть первым выполнится i++, тогда получим 5 + 7 = 12 > Пусть первым выполнится ++i, тогда получим 6 + 6 = 12 > Как получить 10, 11, 13 и 14?
E>С точками следования я знаком и понимаю, что ++i и i++ могут быть вычислены в любом порядке, но до операции сложения. Однако как получить 11, все равно не ясно
у тебя изначально порочный подход, никто не обещает "выполнять" i++ за один присест -- этот инкремент может быть отложен до любого момента до следующей точки следования, более того он может выполняться асинхронно.
Более того, в этом выражении неопределенное поведение, а значит программа имеет право выполнить что угодно, хоть отформатировать диск.
Здравствуйте, watchyourinfo, Вы писали:
W>Более того, в этом выражении неопределенное поведение, а значит программа имеет право выполнить что угодно, хоть отформатировать диск.
хотел бы я посмотреть как тебе понравится что из-за ошибки в использовании глоб. переменных в программе у тебя форматируется винт
Здравствуйте, BulatZiganshin, Вы писали:
W>>Более того, в этом выражении неопределенное поведение, а значит программа имеет право выполнить что угодно, хоть отформатировать диск. BZ>хотел бы я посмотреть как тебе понравится что из-за ошибки в использовании глоб. переменных в программе у тебя форматируется винт
Ну тебя ж предупреждали: может произойти всё что угодно.
Здравствуйте, Masterkent, Вы писали: M>Раз уж завели такой топик, покажу один из подарочков, уготованных нам комитетом по стандартизации C++: M>http://ideone.com/CoyN7 M>Howard Hinnant из Library Working Group, с которым я недавно беседовал, не видит ничего плохого в том, что у std::pair и std::tuple при инстанцировании их ссылочными типами copy/move конструкторы делают совсем не то же самое, что copy/move операторы присваивания, и вот такое необычное поведение программы его, похоже, полностью устраивает.
Логично. Инициализация ссылки и присваивание ссылке — совершенно разные вещи. Если вам такое поведение кажется необычным — не используйте кортежи ссылок.
Здравствуйте, watchyourinfo, Вы писали:
W>Более того, в этом выражении неопределенное поведение, а значит программа имеет право выполнить что угодно, хоть отформатировать диск.
Именно поэтому там написано "где-то от 10 до 14"
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
gegMOPO4:
M>>Раз уж завели такой топик, покажу один из подарочков, уготованных нам комитетом по стандартизации C++: M>>http://ideone.com/CoyN7 M>>Howard Hinnant из Library Working Group, с которым я недавно беседовал, не видит ничего плохого в том, что у std::pair и std::tuple при инстанцировании их ссылочными типами copy/move конструкторы делают совсем не то же самое, что copy/move операторы присваивания, и вот такое необычное поведение программы его, похоже, полностью устраивает.
MOP>Логично. Инициализация ссылки и присваивание ссылке — совершенно разные вещи.
Объект и ссылка — совершенно разные вещи. pair<T1, T2> и tuple<Types...> — обычные объектные типы, и их экземплярам следует вести себя соответственно.
MOP>Если вам такое поведение кажется необычным — не используйте кортежи ссылок.
Оно-то, конечно, понятно, что если библиотека спроектирована через ж., её можно не использовать — собственно, поэтому разрешение core issues в текущей спецификации C++0x для меня представляет гораздо больший интерес, чем разрешение большинства library issues. Вопрос в том, зачем проектировать библиотеку через ж., когда есть возможность сделать всё по-людски. Если некая операция не является присваиванием в обычном понимании, то следует хорошенько подумать над тем, стоит ли её реализовывать в виде функции operator=. При надлежащей реализации tuple и vector в случае моего примера можно добиться ожидаемого поведения программы во время выполнения или хотя бы диагностики нарушения предусловий во время компиляции.
Здравствуйте, Masterkent, Вы писали: M>gegMOPO4: MOP>>Логично. Инициализация ссылки и присваивание ссылке — совершенно разные вещи. M>Объект и ссылка — совершенно разные вещи. pair<T1, T2> и tuple<Types...> — обычные объектные типы, и их экземплярам следует вести себя соответственно.
Значит не «обычные», если содержат ссылки.
MOP>>Если вам такое поведение кажется необычным — не используйте кортежи ссылок. M>Оно-то, конечно, понятно, что если библиотека спроектирована через ж., её можно не использовать — собственно, поэтому разрешение core issues в текущей спецификации C++0x для меня представляет гораздо больший интерес, чем разрешение большинства library issues. Вопрос в том, зачем проектировать библиотеку через ж., когда есть возможность сделать всё по-людски. Если некая операция не является присваиванием в обычном понимании, то следует хорошенько подумать над тем, стоит ли её реализовывать в виде функции operator=.
Конструирование кортежа — это вызов соответствующих конструкторов элементов. Для ссылки это — связывание с другим объектом (на всю жизнь). Логично? Логично.
Присваивание кортежу — это присваивание элементам. Для ссылки присваивание приводит к изменению значения связанного объекта. В этом смысл ссылок, если вам это не нужно, используйте указатели.
Вставка в середину вектора приводит к конструированию нового элемента за пределами старого вектора (где ничего не было) и присвоению новых значений выше места вставки (потому, что там уже были сконструированные объекты).
Мне всё понятно, всё выглядит последовательным и логичным. Если это не то, что вам нужно, используйте указатели, вместо ссылок.
M> При надлежащей реализации tuple и vector в случае моего примера можно добиться ожидаемого поведения программы во время выполнения или хотя бы диагностики нарушения предусловий во время компиляции.
Как? Как вы это реализуете, а главное, как обоснуете?
Здравствуйте, gegMOPO4, Вы писали:
MOP>Здравствуйте, Masterkent, Вы писали: M>>gegMOPO4: MOP>>>Логично. Инициализация ссылки и присваивание ссылке — совершенно разные вещи. M>>Объект и ссылка — совершенно разные вещи. pair<T1, T2> и tuple<Types...> — обычные объектные типы, и их экземплярам следует вести себя соответственно. MOP>Значит не «обычные», если содержат ссылки.
Причём здесь это? Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
СВВ>Авторы: СВВ> Старостин Василий Викторович
СВВ>Аннотация: СВВ>Несколько веселых и интересных примеров на языке C++.
Еще можно так:
int i = 0;
A a(i);
а теперь представим, что мы пишем шаблонный код, и тем там тип неизвестен.
Зато мы знаем, что инициализация с пустыми скобочками означает нулевую инициализацию в случае фундаментальных типов, и конструктор по умолчанию для классов.
Поэтому мы пишем так:
Здравствуйте, Vain, Вы писали: V>Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы.
Нет, это проблемы типа элементов вектора. Вы ведь не будете предъявлять претензии к вектору за то, что в нём нельзя хранить auto_ptr?
Если мы создаём объект на месте, где ничего не было — вызывается конструктор. Если на месте другого объекта — оператор присваивания. В этом их разница.
Здравствуйте, gegMOPO4, Вы писали:
MOP>Здравствуйте, Vain, Вы писали: V>>Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы. MOP>Нет, это проблемы типа элементов вектора. Вы ведь не будете предъявлять претензии к вектору за то, что в нём нельзя хранить auto_ptr?
Как раз с автопоинтером поведение очевидно.
MOP>Если мы создаём объект на месте, где ничего не было — вызывается конструктор. Если на месте другого объекта — оператор присваивания. В этом их разница.
Ну так половина конструкций с вектором работать не будет, а оно нафига надо? Можно было давно уже пофиксить, к примеру, вызывать вместо оператора присваивания — конструктор копирования и деструктор.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Vain, Вы писали: V>Здравствуйте, gegMOPO4, Вы писали: MOP>>Здравствуйте, Vain, Вы писали: V>>>Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы. MOP>>Нет, это проблемы типа элементов вектора. Вы ведь не будете предъявлять претензии к вектору за то, что в нём нельзя хранить auto_ptr? V>Как раз с автопоинтером поведение очевидно.
Для вас это просто привычно. Потому, что жужжат об этом на каждом углу. На самом деле очевидность auto_ptr и tuple<&> совершенно одинакова. Лет через десять это станет очевидно всем.
MOP>>Если мы создаём объект на месте, где ничего не было — вызывается конструктор. Если на месте другого объекта — оператор присваивания. В этом их разница. V>Ну так половина конструкций с вектором работать не будет, а оно нафига надо? Можно было давно уже пофиксить, к примеру, вызывать вместо оператора присваивания — конструктор копирования и деструктор.
Это vector<auto_ptr<>> — половина конструкций? Только в примерах «как делать нельзя». Почему-то у тех, кто понимает, как оно работает, всё раньше прекрасно работало. И будет работать. А вот ваше предложение весьма вероятно сломает кучу кода.
Здравствуйте, gegMOPO4, Вы писали:
V>>>>Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы. MOP>>>Нет, это проблемы типа элементов вектора. Вы ведь не будете предъявлять претензии к вектору за то, что в нём нельзя хранить auto_ptr? V>>Как раз с автопоинтером поведение очевидно. MOP>Для вас это просто привычно. Потому, что жужжат об этом на каждом углу. На самом деле очевидность auto_ptr и tuple<&> совершенно одинакова. Лет через десять это станет очевидно всем.
Как раз таки очевидность не одинакова.
MOP>>>Если мы создаём объект на месте, где ничего не было — вызывается конструктор. Если на месте другого объекта — оператор присваивания. В этом их разница. V>>Ну так половина конструкций с вектором работать не будет, а оно нафига надо? Можно было давно уже пофиксить, к примеру, вызывать вместо оператора присваивания — конструктор копирования и деструктор. MOP>Это vector<auto_ptr<>> — половина конструкций?
Ну так vector<void> тоже не входит.
MOP>Только в примерах «как делать нельзя». Почему-то у тех, кто понимает, как оно работает, всё раньше прекрасно работало. И будет работать. А вот ваше предложение весьма вероятно сломает кучу кода.
Оно даже на скомпилируется при использовании, а вот std::vector<blabla<&> > сломает.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
gegMOPO4:
MOP>Присваивание кортежу — это присваивание элементам. Для ссылки присваивание приводит к изменению значения связанного объекта.
В большинстве случаев семантика копирования для копирующих конструкторов и для копирующих операторов присваивания едина, и, в частности, некоторые функции std::vector-а полагаются на это свойство. Двойственная семантика копирования — потенциальный источник ошибок. В CWG (Core Working Group) это, похоже, хорошо понимают.
struct X
{
explicit X(int &n) : m(n) {}
int &m;
};
int main()
{
int i = 0;
X x1(i);
X x2 = x1;
x2 = x1;
}
Здесь попытка присваивания x2 = x1 должна диагностироваться. Если хочется экзотического почленного присваивания, его придётся реализовать явно.
В LWG к диагностике потенциальных и даже явных ошибок относятся весьма легкомысленно (как, в общем-то, и к документированию стандартной библиотеки), так что убеждать её представителей в целесообразности замены operator= на другую функцию, похоже, бесполезно.
MOP>В этом смысл ссылок, если вам это не нужно, используйте указатели.
А ещё лучше не использовать такие библиотечные средства вообще.
MOP>Вставка в середину вектора приводит к конструированию нового элемента за пределами старого вектора (где ничего не было) и присвоению новых значений выше места вставки (потому, что там уже были сконструированные объекты).
MOP>Мне всё понятно, всё выглядит последовательным и логичным.
В данном случае insert не выполняет ту операцию, для осуществления которой данная функция предназначена. То, что её поведение можно объяснить, вникая в детали реализации, я не считаю свидетельством "последовательности и логичности" дизайна библиотеки.
M>> При надлежащей реализации tuple и vector в случае моего примера можно добиться ожидаемого поведения программы во время выполнения или хотя бы диагностики нарушения предусловий во время компиляции.
MOP>Как? Как вы это реализуете, а главное, как обоснуете?
Во-первых, следует убрать потенциально опасный оператор присваивания, допускающий работу со ссылками, и вместо него ввести другую функцию (желание попользоваться экзотическим присваиванием будет куда более явным). Сделать это можно очень просто: достаточно удалить следующие явные объявления вместе с соответствующими определениями операторных функций:
и нужные специальные функции будут сгенерированы неявно.
Также следует запретить опасный swap при инстанцировании ссылочными типами.
Во-вторых, перемещение элементов в std::vector можно выполнять с помощью деструктора и не бросающего исключений move-конструктора (когда std::is_move_assignable<element_type>::value == false && std::is_nothrow_move_constructible<element_type>::value == true). Такой подход позволил бы расширить применимость вектора.
Здравствуйте, Masterkent, Вы писали: M>gegMOPO4: MOP>>Присваивание кортежу — это присваивание элементам. Для ссылки присваивание приводит к изменению значения связанного объекта. M>В большинстве случаев семантика копирования для копирующих конструкторов и для копирующих операторов присваивания едина, и, в частности, некоторые функции std::vector-а полагаются на это свойство. Двойственная семантика копирования — потенциальный источник ошибок. В CWG (Core Working Group) это, похоже, хорошо понимают.
Следовательно, на параметры std::vector должно накладываться ещё одно ограничение. А вернее, только на некоторые операции.
M>
struct X
M>{
M> explicit X(int &n) : m(n) {}
M> int &m;
M>};
M>int main()
M>{
M> int i = 0;
M> X x1(i);
M> X x2 = x1;
M> x2 = x1;
M>}
M>Здесь попытка присваивания x2 = x1 должна диагностироваться. Если хочется экзотического почленного присваивания, его придётся реализовать явно.
Может быть стоило и присваивание кортежей со ссылками запретить. Но, возможно, у необходимости присваивания были веские причины. Подозреваю, что присваивание кортежу со ссылками станет идиомой и одним из основных применений кортежей со ссылками.
M>В LWG к диагностике потенциальных и даже явных ошибок относятся весьма легкомысленно (как, в общем-то, и к документированию стандартной библиотеки), так что убеждать её представителей в целесообразности замены operator= на другую функцию, похоже, бесполезно.
Это нарушит совместимость.
MOP>>В этом смысл ссылок, если вам это не нужно, используйте указатели. M>А ещё лучше не использовать такие библиотечные средства вообще.
Так никто не заставляет стрелять себе в ногу. Но ружьё есть.
MOP>>Вставка в середину вектора приводит к конструированию нового элемента за пределами старого вектора (где ничего не было) и присвоению новых значений выше места вставки (потому, что там уже были сконструированные объекты). MOP>>Мне всё понятно, всё выглядит последовательным и логичным. M>В данном случае insert не выполняет ту операцию, для осуществления которой данная функция предназначена. То, что её поведение можно объяснить, вникая в детали реализации, я не считаю свидетельством "последовательности и логичности" дизайна библиотеки.
А я считаю, что кортежи со ссылками для этой операции нарушают некоторый контракт, который должен быть явно описан.
M>>> При надлежащей реализации tuple и vector в случае моего примера можно добиться ожидаемого поведения программы во время выполнения или хотя бы диагностики нарушения предусловий во время компиляции. MOP>>Как? Как вы это реализуете, а главное, как обоснуете? M>Во-первых, следует убрать потенциально опасный оператор присваивания, допускающий работу со ссылками, и вместо него ввести другую функцию (желание попользоваться экзотическим присваиванием будет куда более явным). Сделать это можно очень просто: достаточно удалить следующие явные объявления вместе с соответствующими определениями операторных функций: M>
M>и нужные специальные функции будут сгенерированы неявно.
А как вы обеспечите std::copy в контейнер с кортежами ссылок? Изменение объектов, на которые ссылаются, а не переназначение ссылок является требуемым поведением. Иначе бы использовали указатели, а не ссылки.
M>Во-вторых, перемещение элементов в std::vector можно выполнять с помощью деструктора и не бросающего исключений move-конструктора (когда std::is_move_assignable<element_type>::value == false && std::is_nothrow_move_constructible<element_type>::value == true). Такой подход позволил бы расширить применимость вектора.
Кто даст гарантии, что это не поломает существующего кода? Вызов деструктора уж всяко приведёт к регрессу производительности. На это пойти нельзя.
Может быть и можно, расписав хитрые специализации, добиться, чтобы для старого кода работало по-старому, а для нового, где нужно, по-новому. Но это отодвинет стандартизацию ещё лет на пять и сделает и так непростую логику ещё сложнее и запутаннее. Проще огородить заборчиками контрактов и расставить знаки UB.
gegMOPO4:
M>>В большинстве случаев семантика копирования для копирующих конструкторов и для копирующих операторов присваивания едина, и, в частности, некоторые функции std::vector-а полагаются на это свойство. Двойственная семантика копирования — потенциальный источник ошибок. В CWG (Core Working Group) это, похоже, хорошо понимают.
MOP>Следовательно, на параметры std::vector должно накладываться ещё одно ограничение. А вернее, только на некоторые операции.
Так-то оно так, да только статически проверить неидентичность семантики копирования у конструктора и оператора присваивания в общем случае не представляется возможным. А вот статически проверить отсутствие оператора присваивания очень даже просто.
MOP>Может быть стоило и присваивание кортежей со ссылками запретить. Но, возможно, у необходимости присваивания были веские причины. Подозреваю, что присваивание кортежу со ссылками станет идиомой и одним из основных применений кортежей со ссылками.
И что, обязательно использовать именно = ?
M>>В LWG к диагностике потенциальных и даже явных ошибок относятся весьма легкомысленно (как, в общем-то, и к документированию стандартной библиотеки), так что убеждать её представителей в целесообразности замены operator= на другую функцию, похоже, бесполезно.
MOP>Это нарушит совместимость.
Совместимость с C++03 это не нарушит. В C++03 tuple-ов вообще нет, а pair-ы со ссылками запрещены.
MOP>А как вы обеспечите std::copy в контейнер с кортежами ссылок? Изменение объектов, на которые ссылаются, а не переназначение ссылок является требуемым поведением.
Если такие ситуации редки, можно использовать циклы. Если такое нужно часто, можно ввести прокси-итераторы, для которых *iter = x эквивалентно iter.referenced_object().assignment_function(x).
M>>Во-вторых, перемещение элементов в std::vector можно выполнять с помощью деструктора и не бросающего исключений move-конструктора (когда std::is_move_assignable<element_type>::value == false && std::is_nothrow_move_constructible<element_type>::value == true). Такой подход позволил бы расширить применимость вектора.
MOP>Кто даст гарантии, что это не поломает существующего кода?
При std::is_move_assignable<element_type>::value == false insert у vector-а на данный момент вообще не обязан работать (предусловие не соблюдается). Это достаточно весомый аргумент?
MOP>Вызов деструктора уж всяко приведёт к регрессу производительности.
С чего бы это вдруг?
MOP>Может быть и можно, расписав хитрые специализации, добиться, чтобы для старого кода работало по-старому, а для нового, где нужно, по-новому. Но это отодвинет стандартизацию ещё лет на пять и сделает и так непростую логику ещё сложнее и запутаннее.
Да там совсем незначительные дополнения надо внести. Я хоть и очень невысокого мнения об LWG, но всё же сомневаюсь, что решение таких вопросов у них могло бы занять столько времени.
MOP>Проще огородить заборчиками контрактов и расставить знаки UB.
Здравствуйте, Masterkent, Вы писали:
M>Раз уж завели такой топик, покажу один из подарочков, уготованных нам комитетом по стандартизации C++: M>http://ideone.com/CoyN7 M>Howard Hinnant из Library Working Group, с которым я недавно беседовал, не видит ничего плохого в том, что у std::pair и std::tuple при инстанцировании их ссылочными типами copy/move конструкторы делают совсем не то же самое, что copy/move операторы присваивания, и вот такое необычное поведение программы его, похоже, полностью устраивает.
Кстати, оно фиксится вот так:
#include <vector>
#include <new>
struct test
{
int _i;
int& _r;
test() : _i(-1),_r(_i) {} //Just to resolve compilation error in case of reference in class member.
test(int& r) : _i(0),_r(r) {}
test(const test& t) : _i(0),_r(t._r) {}
void operator=(const test& o)
{
this->~test();
new (this) test(o);
}
};
int static_arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int main(void)
{
std::vector<test> arr;
for(int i = 0; i < 10; i++)
{
arr.push_back(test(static_arr[i]));
}
for(int i = 0; i < 10; i++)
{
printf("%i - %i\n",i,arr[i]._r);
}
printf("\n");
arr.insert(arr.begin()+5,static_arr[9]);
for(int i = 0; i < 11; i++)
{
printf("%i - %i\n",i,arr[i]._r);
}
return 0;
}
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
#include <iostream>
#include <new>
struct test
{
int _i;
int& _r;
test() : _i(-1),_r(_i) {} //Just to resolve compilation error in case of reference in class member.
test(int& r) : _i(0),_r(r) {}
test(const test& t) : _i(0),_r(t._r) {}
void operator=(const test& o)
{
this->~test();
new (this) test(o);
}
};
int main()
{
test t;
int n = 1;
std::cout << t._r << std::endl; // вывод: -1
t = test(n);
std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1
}
Здравствуйте, Masterkent, Вы писали:
M> t = test(n); M> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1
Не может, там конструктор копии вызывается.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Vain:
M>> t = test(n); M>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1 V>Не может, там конструктор копии вызывается.
Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый.
Здравствуйте, Masterkent, Вы писали:
M>>> t = test(n); M>>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1 V>>Не может, там конструктор копии вызывается. M>Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый.
Почему?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Vain:
M>>>> t = test(n); M>>>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1 V>>>Не может, там конструктор копии вызывается. M>>Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый. V>Почему?
Видимо, в комитете по стандартизации посчитали, что оптимизация обращения к ссылкам и константным полям класса более полезна, чем такие игры со временем жизни объектов.
Здравствуйте, Masterkent, Вы писали:
M>>>>> t = test(n); M>>>>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1 V>>>>Не может, там конструктор копии вызывается. M>>>Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый. V>>Почему? M>Видимо, в комитете по стандартизации посчитали, что оптимизация обращения к ссылкам и константным полям класса более полезна, чем такие игры со временем жизни объектов.
Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый.
а реальный пример можно?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]