Re[7]: Занимательный C++
От: Vain Россия google.ru
Дата: 22.03.11 14:07
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Re[8]: Занимательный C++
От: gegMOPO4  
Дата: 22.03.11 14:28
Оценка:
Здравствуйте, Vain, Вы писали:
V>Здравствуйте, gegMOPO4, Вы писали:
MOP>>Здравствуйте, Vain, Вы писали:
V>>>Речь идёт про невозможность использования связки vector+tuple<&> из-за различного поведения vector'а в зависимости от алгоритма размещения по памяти. Произошло перевыделение — вызываем конструкторы, не произошло — операторы??? А кто сказал что они одинаково работают вообще? vector? Ну так его и проблемы.
MOP>>Нет, это проблемы типа элементов вектора. Вы ведь не будете предъявлять претензии к вектору за то, что в нём нельзя хранить auto_ptr?
V>Как раз с автопоинтером поведение очевидно.

Для вас это просто привычно. Потому, что жужжат об этом на каждом углу. На самом деле очевидность auto_ptr и tuple<&> совершенно одинакова. Лет через десять это станет очевидно всем.

MOP>>Если мы создаём объект на месте, где ничего не было — вызывается конструктор. Если на месте другого объекта — оператор присваивания. В этом их разница.

V>Ну так половина конструкций с вектором работать не будет, а оно нафига надо? Можно было давно уже пофиксить, к примеру, вызывать вместо оператора присваивания — конструктор копирования и деструктор.

Это vector<auto_ptr<>> — половина конструкций? Только в примерах «как делать нельзя». Почему-то у тех, кто понимает, как оно работает, всё раньше прекрасно работало. И будет работать. А вот ваше предложение весьма вероятно сломает кучу кода.
Re[9]: Занимательный C++
От: Vain Россия google.ru
Дата: 22.03.11 15:40
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Re[5]: Занимательный C++
От: Masterkent  
Дата: 22.03.11 17:38
Оценка:
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>Как? Как вы это реализуете, а главное, как обоснуете?


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

tuple(const tuple&) = default;
tuple(tuple&&) = default;

tuple& operator=(const tuple&);
tuple& operator=(tuple&&) noexcept;

и нужные специальные функции будут сгенерированы неявно.

Также следует запретить опасный swap при инстанцировании ссылочными типами.

Во-вторых, перемещение элементов в std::vector можно выполнять с помощью деструктора и не бросающего исключений move-конструктора (когда std::is_move_assignable<element_type>::value == false && std::is_nothrow_move_constructible<element_type>::value == true). Такой подход позволил бы расширить применимость вектора.
Re[6]: Занимательный C++
От: gegMOPO4  
Дата: 22.03.11 18:48
Оценка:
Здравствуйте, 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>
tuple(const tuple&) = default;
M>tuple(tuple&&) = default;
M>tuple& operator=(const tuple&);
M>tuple& operator=(tuple&&) noexcept;

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.
Re[7]: Занимательный C++
От: Masterkent  
Дата: 22.03.11 20:17
Оценка:
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.


И получится халтура.
Re: Занимательный C++
От: maykie Россия  
Дата: 23.03.11 07:19
Оценка:
Здравствуйте, Старостин Василий Викторович, Вы писали:

const int x = 1;
const int y = 2;
struct j
{
   int j;
};
namespace i
{
   int x[x];
   int y = y;
   j j;
   int i::i = j.j;
};


Comeau(http://www.comeaucomputing.com/tryitout/) и gcc(http://codepad.org/8qLHULQG) не компилируют i::i
Re[3]: Занимательный C++
От: Mazay Россия  
Дата: 23.03.11 08:42
Оценка: +1
Здравствуйте, jyuyjiyuijyu, Вы писали:

СВВ>>>Аннотация:

СВВ>>>Несколько веселых и интересных примеров на языке C++.

PD>>Единственное, что в этих примерах занимательного — это детское удивление автора, впервые открывшего для себя язык С++, и спешащего поведать о своем удивлении всему миру.


J>К молодым людям нельзя относиться свысока.


Ну автора можно понять. По-настоящему удивительно, почему такую статью приняли к публикации .
Главное гармония ...
Re: Занимательный C++
От: Yuki-no Tenshi Украина  
Дата: 23.03.11 08:54
Оценка:
Еще можно было бы упомянуть, что для неполных типов не имеет разницы используем мы ключевое слово class или struct


#include<iostream>
using std::cout;
using std::endl;

class X;

X* xPtr = 0;

struct X
{
    int blaBla;
};

int main()
{
    X x;
    xPtr = &x;
    return 0;
}



http://codepad.org/2rfFPeR3
雪の天使
Re[2]: Занимательный C++
От: Vain Россия google.ru
Дата: 23.04.11 09:53
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Re[3]: Занимательный C++
От: Masterkent  
Дата: 24.04.11 06:48
Оценка:
Vain:

V>Кстати, оно фиксится вот так:


Я не понял, что там фиксится.

#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
}
Re[4]: Занимательный C++
От: Vain Россия google.ru
Дата: 24.04.11 08:58
Оценка:
Здравствуйте, Masterkent, Вы писали:

V>>Кстати, оно фиксится вот так:

M>Я не понял, что там фиксится.
Оператор работает как конструктор копии.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[4]: Занимательный C++
От: Vain Россия google.ru
Дата: 24.04.11 09:13
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Re[5]: Занимательный C++
От: Masterkent  
Дата: 24.04.11 11:38
Оценка:
Vain:

M>> t = test(n);

M>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1
V>Не может, там конструктор копии вызывается.

Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый.
Re[6]: Занимательный C++
От: Vain Россия google.ru
Дата: 24.04.11 15:27
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Re[7]: Занимательный C++
От: Masterkent  
Дата: 25.04.11 04:24
Оценка:
Vain:

M>>>> t = test(n);

M>>>> std::cout << t._r << std::endl; // undefined behavior (3.8/7), имеет право снова вывести -1
V>>>Не может, там конструктор копии вызывается.
M>>Нолик более вероятен, но сути это не меняет — в данном случае поведение не определено. Компилятор вправе считать, что оба выражения t._r ссылаются на один и тот же адрес, несмотря на то, что объект t уничтожили и на его месте создали новый.
V>Почему?

Видимо, в комитете по стандартизации посчитали, что оптимизация обращения к ссылкам и константным полям класса более полезна, чем такие игры со временем жизни объектов.
Re[8]: Занимательный C++
От: Vain Россия google.ru
Дата: 25.04.11 06:18
Оценка:
Здравствуйте, 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.]
[Даю очевидные ответы на риторические вопросы]
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.