про перемещение вопрос
От: borya_ilin  
Дата: 17.05.19 09:29
Оценка:
Господа, если не сложно, напомните мне пожалуйста, как мы поступаем в такой ситуации в этом нашем новом c++?

struct Bar {
  void set_foos(const std::vector<Foo> &foos) {} // 1)
};

viod f()
{
  std::vector<Foo> foos;
  
  while (!done) {
    if (cond1) {
      Foo foo;
      foo.load_from(file);
      foos.push_back(foo);
    }

    if (cond2) {
      cur_bar.set_foos(foos);  // 2)
      foos.clear();
    }
  }
}


Я хочу чтобы содержимое вектора foos премещалось в cur_bar, а не копировалось
И при этом чтобы foos очищался и его можно было продолжать использовть
Какой интерфейс должен быть в 1) и что делать в 2) ?
Re: про перемещение вопрос
От: B0FEE664  
Дата: 17.05.19 09:43
Оценка: 2 (1)
Здравствуйте, borya_ilin, Вы писали:

_>Я хочу чтобы содержимое вектора foos премещалось в cur_bar, а не копировалось

_>И при этом чтобы foos очищался и его можно было продолжать использовть
_>Какой интерфейс должен быть в 1) и что делать в 2) ?

Если речь об оптимизации по скорости, то замена set_foos(const std::vector<Foo> &foos) на swap_foos(std::vector<Foo>& foos) { member.swap(foos); ... } будет, ИМХО, наилучшей.
И каждый день — без права на ошибку...
Re: про перемещение вопрос
От: koenjihyakkei Россия  
Дата: 17.05.19 09:46
Оценка: 2 (1)
Здравствуйте, borya_ilin, Вы писали:

Навскидку (возможно не прав):

struct Bar {
  void set_foos(std::vector<Foo> &&foos) {} // 1)
};

viod f()
{
  std::vector<Foo> foos;
  
  while (!done) {
    if (cond1) {
      Foo foo;
      foo.load_from(file);
      foos.push_back(foo);
    }

    if (cond2) {
      cur_bar.set_foos(std::move(foos));  // 2)
    }
  }
}
Re: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 09:51
Оценка: 2 (1)
Здравствуйте, borya_ilin, Вы писали:

_>Господа, если не сложно, напомните мне пожалуйста, как мы поступаем в такой ситуации в этом нашем новом c++?


_>
_>struct Bar {
_>  void set_foos(const std::vector<Foo> &foos) {} // 1)
_>};

_>viod f()
_>{
_>  std::vector<Foo> foos;
  
_>  while (!done) {
_>    if (cond1) {
_>      Foo foo;
_>      foo.load_from(file);
_>      foos.push_back(foo);
_>    }

_>    if (cond2) {
_>      cur_bar.set_foos(foos);  // 2)
_>      foos.clear();
_>    }
_>  }
_>}
_>


_>Я хочу чтобы содержимое вектора foos премещалось в cur_bar, а не копировалось

_>И при этом чтобы foos очищался и его можно было продолжать использовть

_>Какой интерфейс должен быть в 1)


struct Bar {
  void set_foos(const std::vector<Foo> &foos) {} // 1)

  void set_foos(std::vector<Foo> &&foos){ //1a. Эти две перегрузки могут существовать одновременно
     m_foos = std::move(foos); // Использование move принциаиально - без него будет копирование, а не перемещение
  }
private:
  std::vector<Foo> m_foos;
};


Если set_foos делает что-то более сложное, чем перемещающее присваивание, то тут уже нужно смотреть отдельно.

_>и что делать в 2) ?


   cur_bar.set_foos(std::move(foos));  // 2)
   foos.clear(); // Это нужно для гарантии, что foos пустой.


Если set_foos выполняет перемещение содержимого foos, то вызов clear не является необходимым. Но мы ведь можем и не знать, что там делается внутри set_foos, поэтому лучше вызвать clear, во избежание "сюрпризов".
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 12:11
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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


_>>Я хочу чтобы содержимое вектора foos премещалось в cur_bar, а не копировалось

_>>И при этом чтобы foos очищался и его можно было продолжать использовть
_>>Какой интерфейс должен быть в 1) и что делать в 2) ?

BFE>Если речь об оптимизации по скорости, то замена set_foos(const std::vector<Foo> &foos) на swap_foos(std::vector<Foo>& foos) { member.swap(foos); ... } будет, ИМХО, наилучшей.


Ну, с учетом того, что старое содержимое никому не нужно, и контейнер все равно будет очищаться, можно без потерь эту очистку поместить внутрь функции, что позволит сохранить ее семантику и имя без изменений. И что там будет внутри делаться — swap или перемещение — это уже детали реализации самой функции.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 17.05.2019 12:12 rg45 . Предыдущая версия .
Re[3]: про перемещение вопрос
От: B0FEE664  
Дата: 17.05.19 12:30
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну, с учетом того, что старое содержимое никому не нужно, и контейнер все равно будет очищаться, можно без потерь эту очистку поместить внутрь функции, что позволит сохранить ее семантику и имя без изменений. И что там будет внутри делаться — swap или перемещение — это уже детали реализации самой функции.


Насколько я помню, большинство реализаций вектора не отказываются от аллокированной памяти при удалении элементов (даже shrink_to_fit() не даёт гарантий), поэтому организовав ping-pong можно выиграть (и нельзя проиграть) на количестве аллокаций памяти.
И каждый день — без права на ошибку...
Re: про перемещение вопрос
От: Igore Россия  
Дата: 17.05.19 14:18
Оценка: 2 (1) +1
Здравствуйте, borya_ilin, Вы писали:

_>Господа, если не сложно, напомните мне пожалуйста, как мы поступаем в такой ситуации в этом нашем новом c++?


_>
_>      foos.push_back(foo);//3
_>


_>Я хочу чтобы содержимое вектора foos премещалось в cur_bar, а не копировалось

_>И при этом чтобы foos очищался и его можно было продолжать использовть
_>Какой интерфейс должен быть в 1) и что делать в 2) ?
Можно и от этого избавиться, например так
foos.emplace_back();
foos.back()->load_from(file);
Re[4]: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 14:48
Оценка:
Здравствуйте, B0FEE664, Вы писали:

R>>Ну, с учетом того, что старое содержимое никому не нужно, и контейнер все равно будет очищаться, можно без потерь эту очистку поместить внутрь функции, что позволит сохранить ее семантику и имя без изменений. И что там будет внутри делаться — swap или перемещение — это уже детали реализации самой функции.


BFE>Насколько я помню, большинство реализаций вектора не отказываются от аллокированной памяти при удалении элементов (даже shrink_to_fit() не даёт гарантий), поэтому организовав ping-pong можно выиграть (и нельзя проиграть) на количестве аллокаций памяти.


Так и есть. Но при этом совсем не обязательно выставлять swap наружу и менять имя и семантику функции:

struct Bar {

  void set_foos(std::vector<Foo> &&foos) {
     
    swap(m_foos, foos);
    foos.clear(); // элементы удаляются, от памяти не отказываемся, пинг-понг жив (с)
  }

private:
  std::vector<Foo> m_foos;
};
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: про перемещение вопрос
От: B0FEE664  
Дата: 17.05.19 15:07
Оценка:
Здравствуйте, rg45, Вы писали:

R>Так и есть. Но при этом совсем не обязательно выставлять swap наружу и менять имя и семантику функции:


R>
R>struct Bar {

R>  void set_foos(std::vector<Foo> &&foos) {
     
R>    swap(m_foos, foos);
R>    foos.clear(); // элементы удаляются, от памяти не отказываемся, пинг-понг жив (с)
R>  }

R>private:
R>  std::vector<Foo> m_foos;
R>};
R>


1. Такое поведение неожиданно.
2. Зачем тут &&? Достаточно простой ссылки.
И каждый день — без права на ошибку...
Re[6]: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 15:26
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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


R>>Так и есть. Но при этом совсем не обязательно выставлять swap наружу и менять имя и семантику функции:


R>>
R>>struct Bar {

R>>  void set_foos(std::vector<Foo> &&foos) {
     
R>>    swap(m_foos, foos);
R>>    foos.clear(); // элементы удаляются, от памяти не отказываемся, пинг-понг жив (с)
R>>  }

R>>private:
R>>  std::vector<Foo> m_foos;
R>>};
R>>


BFE>1. Такое поведение неожиданно.


Какое "такое"? Функция называется set_foos (а не swap_foos) и производимый ею эффект в точности соответствует ее названию и семантике.

BFE>2. Зачем тут &&? Достаточно простой ссылки.


А вот это как раз будет неожиданно, когда foos вдруг лишится своих элементов в таком сценарии:

Bar bar;
std::vector<Foo> foos {/*. . . */};
bar.set_foos(foos);


Ты не замечаешь, что мы с тобой говорим про две разные функции? Я говорю про исходную set_foos, а ты ее уже мыслено зарефакторил, наделил ее другой семантикой и смотришь на нее как на swap_foos.

Давай я еще раз сформулирую свою позицию: для того, чтобы достичь оптимизации по скорости, о которой ты говоришь здесь
Автор: B0FEE664
Дата: 17.05.19
, не обязательно переделывать set_foos на swap_foos.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: про перемещение вопрос
От: borya_ilin  
Дата: 17.05.19 16:08
Оценка:
да, точняк)
давненько не брал я в руки шашек
тогда ещё никаких emplace_backов не было
хороший стал vector)
Re[2]: про перемещение вопрос
От: borya_ilin  
Дата: 17.05.19 16:10
Оценка: +1
вообще норм, но не очень хотелось бы чтобы реализация начала интерфейсом рулить
Re[2]: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 16:12
Оценка: +2
Здравствуйте, Igore, Вы писали:

I>Можно и от этого избавиться, например так

I>
I>foos.emplace_back();
I>foos.back()->load_from(file);
I>


Только через точку, а не через стрелочку:

foos.back().load_from(file);
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: про перемещение вопрос
От: B0FEE664  
Дата: 17.05.19 16:15
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>1. Такое поведение неожиданно.

R>Какое "такое"?

вызов выглядит так:
bar.set_foos(std::move(foos));

Я ожидаю, что после этого foos не владеет никакой динамической памятью. Впрочем, быть может, это мои проблемы.

BFE>>2. Зачем тут &&? Достаточно простой ссылки.

R>А вот это как раз будет неожиданно, когда foos вдруг лишится своих элементов в таком сценарии:
R>
R>Bar bar;
R>std::vector<Foo> foos {/*. . . */};
R>bar.set_foos(foos);
R>

Тут я согласен, так делать не следует.
Меня смущает другое: семантика перемещения используется не понятно зачем. Какое же это перемещение?
Ещё меня смущает то, что объект используется после того, как его переместили, но это мелочи.

R>Ты не замечаешь, что мы с тобой говорим про две разные функции? Я говорю про исходную set_foos, а ты ее уже мыслено зарефакторил, наделил ее другой семантикой и смотришь на нее как на swap_foos.


Ну это ещё как посмотреть... По мне это у вас поменялась семантика set_foos без изменения имени функции.
Я не против добавления функции set_foos(std::vector<Foo> &&foos) (или, быть может, set_foos(auto&& foos)), но только с ожидаемым поведением.

R>Давай я еще раз сформулирую свою позицию: для того, чтобы достичь оптимизации по скорости, о которой ты говоришь здесь
Автор: B0FEE664
Дата: 17.05.19
, не обязательно переделывать set_foos на swap_foos.


Я бы не стал переделывать, я бы просто добавил бы функцию swap_foos.
И каждый день — без права на ошибку...
Re[8]: про перемещение вопрос
От: rg45 СССР  
Дата: 17.05.19 16:34
Оценка:
Здравствуйте, B0FEE664, Вы писали:

R>>
R>>Bar bar;
R>>std::vector<Foo> foos {/*. . . */};
R>>bar.set_foos(foos);
R>>

BFE>Тут я согласен, так делать не следует.
BFE>Меня смущает другое: семантика перемещения используется не понятно зачем. Какое же это перемещение?

В этом примере семантика перемещения как раз НЕ используется. И в этом случае должна быть выбрана копирующая версия функции set_foos. По семантике две эти перегрузки очень близки к копирующему и перемещающему операторам присваивания. Кстати, перемещающй оператор присваивания вполне может быть реализован при помощи swap, с сохраниением оптимизации "пинг-понг", как ты метко ее окрестил.

BFE>Ещё меня смущает то, что объект используется после того, как его переместили, но это мелочи.


Вот с этим аргументом соглашусь, пожалуй. Но это отдельный холивар. Мы тут уже копья ломали на эту тему, помнится.

R>>Ты не замечаешь, что мы с тобой говорим про две разные функции? Я говорю про исходную set_foos, а ты ее уже мыслено зарефакторил, наделил ее другой семантикой и смотришь на нее как на swap_foos.


BFE>Ну это ещё как посмотреть... По мне это у вас поменялась семантика set_foos без изменения имени функции.


Тот же принцип, по которому одновременно существуют копирующая и перемещающая версии оператора присваивания, ничего неожиданного.

BFE>Я бы не стал переделывать, я бы просто добавил бы функцию swap_foos.


А я бы не стал добавлять ненужные функции.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 17.05.2019 16:50 rg45 . Предыдущая версия . Еще …
Отредактировано 17.05.2019 16:35 rg45 . Предыдущая версия .
Re[9]: про перемещение вопрос
От: B0FEE664  
Дата: 20.05.19 12:30
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>Ну это ещё как посмотреть... По мне это у вас поменялась семантика set_foos без изменения имени функции.

R>Тот же принцип, по которому одновременно существуют копирующая и перемещающая версии оператора присваивания, ничего неожиданного.
Я ожидаю, что после выполнения этого кода:
bar.set_foos(std::move(foos));

у переменной foos не останется динамически аллокированной памяти.
Т.е. я ожидаю, что следующие постусловие метода set_foos(std::vector<Foo>&&) будет соблюдатся:
assert(0 == foos.capacity());
И каждый день — без права на ошибку...
Re[10]: про перемещение вопрос
От: rg45 СССР  
Дата: 20.05.19 13:28
Оценка: +2
Здравствуйте, B0FEE664, Вы писали:

BFE>Я ожидаю, что после выполнения этого кода:

BFE>
BFE>bar.set_foos(std::move(foos));
BFE>

BFE>у переменной foos не останется динамически аллокированной памяти.

С чего вдруг? Ты передал объект по rvalue ссылке, разрешив обращаться с его содержимым как угодно. КАК УГОДНО, понимаешь? Функция может переместить это содержимое, а может скопировать. А может подсунуть туда старое содержимое инкапсулированного поля при помощи swap. Эта функция ничего тебе не обещает сверх того, что заложено в ее семантике. Вот если бы она называлась move или swap, тогда другое дело.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[11]: про перемещение вопрос
От: B0FEE664  
Дата: 20.05.19 14:45
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>Я ожидаю, что после выполнения этого кода:

BFE>>
BFE>>bar.set_foos(std::move(foos));
BFE>>

BFE>>у переменной foos не останется динамически аллокированной памяти.

R>С чего вдруг? Ты передал объект по rvalue ссылке, разрешив обращаться с его содержимым как угодно. КАК УГОДНО, понимаешь? Функция может переместить это содержимое, а может скопировать. А может подсунуть туда старое содержимое инкапсулированного поля при помощи swap.

Это я прекрасно понимаю и принимаю. Меня не смущает, что операция деления может складывать аргументы, а цикл может использоваться как goto, но у того, кто первый раз читает такой код, будут вопросы к автору. Если трюк обоснован и красив, то его можно принять и использовать. Однако, как правило, трюки со скрытыми и неочевидными эффектами не приживаются.

R>Эта функция ничего тебе не обещает сверх того, что заложено в ее семантике. Вот если бы она называлась move или swap, тогда другое дело.

В том-то и дело, что в вызове я вижу семантику перемещениям (std::move как бы намекает), поэтому я ожидаю, что это и есть перемещение, а не что-то ещё.
И каждый день — без права на ошибку...
Re[3]: про перемещение вопрос
От: B0FEE664  
Дата: 20.05.19 15:00
Оценка: :)
Здравствуйте, borya_ilin, Вы писали:

_>вообще норм, но не очень хотелось бы чтобы реализация начала интерфейсом рулить


У std::vector swap в интерфейсе есть, однако.
Впрочем, быть может, swap — неудачное наименование метода. Как насчёт exchange_foos(std::vector<Foo>& foos)? Вроде бы звучит солидно, по корпоративному.
И каждый день — без права на ошибку...
Re[12]: про перемещение вопрос
От: rg45 СССР  
Дата: 20.05.19 18:45
Оценка: +2
Здравствуйте, B0FEE664, Вы писали:

R>>Эта функция ничего тебе не обещает сверх того, что заложено в ее семантике. Вот если бы она называлась move или swap, тогда другое дело.

BFE>В том-то и дело, что в вызове я вижу семантику перемещениям (std::move как бы намекает), поэтому я ожидаю, что это и есть перемещение, а не что-то ещё.

А чего ты ждешь, например, от перемещающего оператора присваивания std::vector? Если также гарантированного перемещения, то совершенно напрасно — все, чего требует стандарт — это оставить объект "in valid but unspecified state". И если ты полистаешь стандарт, то увидишь, что это не какой-то исключительный случай, а обычная практика. Этому в стандарте посвящено даже несколько отдельных параграфов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 20.05.2019 18:57 rg45 . Предыдущая версия . Еще …
Отредактировано 20.05.2019 18:56 rg45 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.