Лучший способ использовать emplace_back, чтобы избежать вызова конструктора перемещения?

Я только что узнал о гарантированном копировании в C++17. Согласно ответу на этот вопрос:

Когда вы делаете return T();, это инициализирует возвращаемое значение функции через prvalue. Поскольку эта функция возвращает T, временное не создается; инициализация prvalue просто напрямую инициализирует возвращаемое значение.

Нужно понимать, что, поскольку возвращаемое значение является prvalue, это еще не объект. Это просто инициализатор объекта, как и T().

Поэтому мне было интересно, работает ли это для чего-либо, кроме:

T f() {return T();}
T t = f();

Поэтому я написал этот код с emplace_back, чтобы проверить его:

#include <vector>
#include <iostream>
struct BigObj{
  BigObj() = default;
  BigObj(int) { std::cout << "int ctor called" << std::endl;  }
  BigObj(const BigObj&){
    std::cout << "copy ctor called" << std::endl;
  }
  BigObj(BigObj&&){
    std::cout << "move ctor called" << std::endl;
  }
};
BigObj f(){ return BigObj(2); }
int g(){ return 2; }
int main(){
  std::vector<BigObj> v;
  v.reserve(10);
  std::cout << "emplace_back with rvalue \n";
  v.emplace_back(1+1);
  std::cout << "emplace_back with f()\n";
  v.emplace_back(f());
  std::cout << "emplace_back with g()\n";
  v.emplace_back(g());
}

Это вывод, который я получаю (с отключенным копированием):

emplace_back with rvalue 
int ctor called
emplace_back with f()
int ctor called
move ctor called
emplace_back with g()
int ctor called

Кажется, что конструктор перемещения по-прежнему вызывается, несмотря на то, что prvalue передается непосредственно в emplace_back, который, как я думал, можно использовать для непосредственного создания объекта вместо использования для создания временного объекта с последующим его перемещением.

Есть ли более элегантный способ избежать вызова конструктора перемещения с помощью emplace_back, кроме как делать что-то вроде того, что делает функция g()?


person user2108462    schedule 10.04.2018    source источник


Ответы (1)


Кажется, что конструктор перемещения все еще вызывается, хотя значение prvalue передается непосредственно в emplace_back.

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

Правильный способ использования emplace_back — передать ему аргументы для инициализации объекта на месте. Таким образом, вам не нужно перемещать тип элемента вектора (хотя вам может потребоваться переместить/скопировать аргументы).

person StoryTeller - Unslander Monica    schedule 10.04.2018
comment
Итак, когда значение prvalue передается в любую функцию, которая принимает ссылку любого типа (ссылку на константу или ссылку на rvalue), по необходимости создается временное? - person user2108462; 10.04.2018
comment
@ user2108462 - Да. Привязка к ссылкам — это место, где произошла бы временная материализация. Это должно произойти, чтобы мы получили объект, на который ссылаются. - person StoryTeller - Unslander Monica; 10.04.2018
comment
@StoryTeller, ах, с моей стороны ошибка понимания прочитанного - сам комментарий на самом деле говорит prvalue. - person Jan Hudec; 10.04.2018
comment
Конечно, было бы круто вызвать v.emplace_back(f);, т.е. не сразу выполнять f(), а передать фабричную функцию в emplace для выполнения в подходящее время. Однако сделать это в общем случае с помощью std::function было бы излишним для простого объекта. - person Gem Taylor; 10.04.2018
comment
@GemTaylor - Разве вы не можете сделать что-то подобное с шаблонами выражений или эквивалентной техникой? Может быть? Однако было бы неплохо, если бы он был доступен без шаблона. - person StoryTeller - Unslander Monica; 10.04.2018
comment
Верно. Я предполагаю, что это будет выглядеть как ужас, который представляет собой пару c++ 11 std:::map emplace или, возможно, std::thread. - person Gem Taylor; 10.04.2018