emplace_back () против push_back при вставке пары в std :: vector - PullRequest
0 голосов
/ 23 декабря 2018

Я определил следующее

std::vector<std::pair<int,int> > my_vec;
my_vec.push_back( {1,2} ); //this works
my_vec.emplace_back( {1,2} ); // this doesn't work
std::pair<int,int> temp_pair = {1,2}; 
my_vec.emplace_back( temp_pair );         //this works

Я компилирую с c ++ 11.Третья строка проблематична, но я думал, что вы можете использовать emplace_back() везде, где у вас есть push_back(), но это, по-видимому, неправильно.Почему не работает третья строка?

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

1) {1, 2} не является выражением

Синтаксис

{1, 2}

очень "странный" по сравнению с другими вещами в C ++.

Обычно в C ++у вас есть выражение (например, x + 1.2), и выражение имеет выведенный тип ... например, если x является переменной int, тип выражения будет double из-за неявного преобразования intdouble и как работает сложение.

Теперь вернемся к {1, 2}: это "странно", потому что, несмотря на то, что выражение выглядит не так, оно не ... это просто синтаксис, и его значение будет зависеть от того, где оно используется.

В некотором смысле ввод здесь будет работать противоположно большинству мест C ++: обычно в C ++ это «in» → «out» (тип «появляется» из компонентов), но здесь «out» → »in«(тип« внедряется »в компоненты).

Текст {1, 2} сам по себе не достаточно значит для компиляции (это может означать разные вещи в зависимости от того, где он используется).

Все это сводится к тому, что {1, 2} нельзя использовать в точности как выражение, даже если правила тщательно разработаны, чтобы обмануть вас.

2) emplace_back принимает параметры конструктора

emplace_back был разработан, чтобывозможность создания объекта непосредственно внутри конечного места в контейнере ... ожидаемые параметры - это параметры конструктора, и это делается для того, чтобы избежать создания временного объекта, просто чтобы иметь возможность сделать копию для конечного пункта назначения и затем выбросить его,Следовательно, ожидаемый параметр emplace_back - это 1 и 2 ... не одно и то же, потому что НЕ построение временного отдельного объекта является именно той причиной, для которой emplace_back был разработан.

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

Последствия

Подводя итог: emplace_back не может использовать {1, 2}, потому что может принимать все (так ненедостаточно контекста), и этот синтаксис не имеет достаточного значения.push_back вместо этого может принять его, потому что он ожидает определенного типа, и это обеспечивает достаточный контекст для интерпретации синтаксиса {1, 2}.Это упрощенное объяснение, но, как обычно, C ++ пошел в направлении еще большей сложности разбора и особых случаев, поэтому я могу понять, почему вам что-то не понятно.

Однако ключевой момент заключается в том, что emplace_back не должен был брать полный объект ... для этого использования push_back.Новая конструкция emplace_back должна использоваться, когда вы хотите передать ПАРАМЕТРЫ КОНСТРУКТОРА для построения конечного объекта в контейнере.

0 голосов
/ 23 декабря 2018

emplace_back принимает пакет параметров с переменными параметрами в качестве аргумента:

template< class... Args >
reference emplace_back( Args&&... args );

Когда вы вызываете его так: emplace_back({1, 2}) вы вызываете его содин аргумент, т.е. {1, 2} и Args не могут быть выведены.Это из-за того, как язык развивался.В C ++ {1, 2} нет типа.Это инициализированный список, заключенный в фигурные скобки, и он может использоваться при определенных типах инициализации, но все они требуют, чтобы тип инициализированного был известен.Вот почему temp_pair = {1,2}; работает, потому что тип temp_pair известен и имеет конструктор, соответствующий (int, int).

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

my_vec.emplace_back(1, 2);

Также обратите внимание, что даже если они работают:

my_vec.emplace_back(std::pair<int, int>{1, 2});
my_vec.emplace_back(temp_pair);   

Они не должны использоваться .Они не добавляют никакого преимущества перед push_back.Весь смысл emplace_back состоит в том, чтобы избежать создания временного T.Все вышеперечисленные вызовы создают временные std::pair<int, int>.


, но я подумал, что вы можете использовать emplace_back() везде, где у вас есть push_back()

Длябольшая часть это правильно.По крайней мере, так было задумано.И вы действительно можете использовать его в своем cese.Вам просто нужно немного изменить синтаксис.Поэтому вместо push_back({1, 2}) вы можете использовать emplace_back(1, 2).

Есть ситуация, когда, к сожалению, вы не можете использовать emplace_back: агрегаты.

struct Agg
{
    int a, b;
};

auto test()
{
    Agg a{1, 2}; // ok, aggregate initialization

    std::vector<Agg> v;
    v.emplace_back(1, 2); // doesn't work :(
}

Это не работаетесли вы не добавите конструктор для Agg.Это считается открытым недостатком в стандарте, но, к сожалению, они не могут найти хорошее решение для этого.Проблема в том, как работает инициализация скобки инициализации, и если вы используете ее в общем коде, вы можете пропустить некоторые конструкторы.Все подробности можно найти в этом замечательном посте: Почему структура агрегированного кода может быть инициализирована скобкой, но не может быть использована с использованием того же списка аргументов, что и при инициализации фигурной скобки?

...