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

Я только что узнал о гарантированном копировании в С++ 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, который, как я думал, можно использовать для прямой конструирования объекта, а не для его создания, а затем для его перемещения.

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

Ответ 1

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

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

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