std :: vector :: emplace_back с выражением lvalue - PullRequest
7 голосов
/ 18 марта 2019

Имеет ли смысл использовать emplace_back с lvalue некоторой структуры S:

как это:

std::vector<S> v;
auto s = S(/*...*/);
v.emplace_back(s);

Вместо просто:

v.emplace_back(/* S constructor arguments */);

или это просто простое неправильное использование emplace_back, которое работает только потому, что const S& (и, следовательно, конструктор копирования) является допустимым экземпляром для Args... args внутри emplace_back, и это явно не запрещено?

Ответы [ 2 ]

5 голосов
/ 18 марта 2019

Как вы уже сказали, передача const S& просто вызовет конструктор копирования.

Если вы не намереваетесь каким-либо образом использовать s перед передачей его в emplace_back, это не обязательно разумно.

Однако, если код для создания s был,например, исключительно длинный, он может улучшить читабельность, чтобы поместить его и код для emplace_back в отдельные строки.Компиляторы чрезвычайно хороши в оптимизации таких случаев и, вероятно, в любом случае будут генерировать один и тот же код (если конструктор копирования используется по умолчанию).Базовый пример: https://godbolt.org/z/D1FClE

Если это улучшает удобочитаемость или удобство обслуживания, сделайте это, в противном случае в нем нет значения.

1 голос
/ 18 марта 2019

Если s позже не требуется в коде, то это неправильное использование функции emplace_back().Это потому, что вы вызываете конструктор копирования класса S вместо передачи аргументов emplace_back(), который будет использовать правильный конструктор из S.

. Рассмотрим следующий код:

#include <iostream>
#include <vector>

struct S
{
    S()          {std::cout<< "     default ctor" <<std::endl;}
    S(int)       {std::cout<< "     user-def ctor" <<std::endl;}
    S(const S &) {std::cout<< "     copy ctor" <<std::endl;}
    S(S &&)      {std::cout<< "     move ctor" <<std::endl;}
};

int main()
{
    std::vector<S> v;
    v.reserve(5);

    std::cout<< "auto calls: " <<std::endl;
    auto s = S();
    std::cout<<std::endl;

    std::cout<< "emplace_back( s ) calls: " <<std::endl;
    v.emplace_back(s);
    std::cout<<std::endl;

    std::cout<< "emplace_back( std::move(s) ) calls: " <<std::endl;
    v.emplace_back(std::move(s));
    std::cout<<std::endl;

    std::cout<< "emplace_back( S{} ) calls: " <<std::endl;
    v.emplace_back(S{});
    std::cout<<std::endl;

    std::cout<< "emplace_back( ) calls: " <<std::endl;
    v.emplace_back();
    std::cout<<std::endl;

    std::cout<< "emplace_back( 2 ) calls: " <<std::endl;
    v.emplace_back(2);
    std::cout<<std::endl;
}

Результаты:

auto calls: 
     default ctor

emplace_back( s ) calls: 
     copy ctor

emplace_back( std::move(s) ) calls: 
     move ctor

emplace_back( S{} ) calls: 
     default ctor
     move ctor

emplace_back( ) calls: 
     default ctor

emplace_back( 2 ) calls: 
     user-def ctor

Резерв используется для выделения пространства на 5 S с.Без резервирования пространства выходные данные будут включать дополнительные вызовы для копирующих векторов из вектора.

Когда вы просто передаете аргументы конструктору S (в данном случае ничего), emplace_back()создает объект S, используя ctor по умолчанию непосредственно внутри вектора.

Кстати, см. пример в godbolt , который является вашим другом в этих случаях, чтобы точно увидеть, что происходит на заднем плане.

...