emplace и конструкторы по умолчанию

Учитывая следующий код, я был удивлен, что try_emplace не смог использовать конструктор по умолчанию, продемонстрированный в первой строке основной функции, вместо этого жалуясь на отсутствие соответствующего вызова функции для Element::Element(double, double). Я неправильно понял, как компилятор создает конструкторы по умолчанию или использование try_emplace? Конечно, я могу заставить этот код работать, определив все параметры ctors для Element, но это кажется излишним.

#include <string>
#include <map>

struct Element
{    
    double a;
    double b;
};

int main(int argc, char** argv)
{
    Element e {2.0, 3.0};

    std::map<std::string, Element> my_map;
    my_map.try_emplace("hello", 2.0, 3.0);

    return 0;
}

person Madden    schedule 21.08.2018    source источник
comment
Компилятор не генерирует c'tor с параметрами, и try_emplace только попытается вызвать c'tor (без агрегатной инициализации).   -  person George    schedule 21.08.2018
comment
Но тогда откуда взялось определение Element e{2.0, 3.0}?   -  person Madden    schedule 21.08.2018
comment
@Madden Element — это агрегат (фактически POD), для которого поддерживается инициализация скобок даже без какого-либо конструктора. Я думаю, что это недостаток стандарта, и для агрегатов должен был быть специальный код в emplace_back. Это было бы более последовательно и менее удивительно.   -  person Michael Veksler    schedule 21.08.2018
comment
По неизвестной причине STL плохо поддерживает агрегаты!   -  person Oliv    schedule 21.08.2018


Ответы (2)


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

Element e(2.0, 3.0);

не компилируется, потому что такого конструктора нет (Element e{2.0, 3.0} обходит такой вызов конструктора). Но это то, что emplace пытается сделать. Чтобы исправить это, вы можете добавить соответствующий конструктор:

struct Element
{
    Element(double a, double b) : a(a), b(b) {}

    double a;
    double b;
};
person lubgr    schedule 21.08.2018

В качестве альтернативы вы можете не определять какие-либо определяемые пользователем ctors в Element, а использовать те, которые все еще определены, несмотря ни на что (если они не удалены явно):

    my_map.try_emplace("hello", Element{2.0, 3.0});
person bipll    schedule 21.08.2018
comment
Повлечет ли этот метод копию объекта Element? - person Madden; 21.08.2018
comment
@ Мэдден - Да. Но поскольку этот тип представляет собой простой агрегат, который легко копируется, он, скорее всего, будет работать не хуже, чем пересылка двух двойников. Не оптимизируйте преждевременно. - person StoryTeller - Unslander Monica; 21.08.2018
comment
Конечно, но это был скорее простой пример - на практике я собираюсь много размещать и с гораздо более сложными/большими объектами. Я определю конкретные конструкторы, чтобы избежать копирования - person Madden; 21.08.2018
comment
@Madden Такие шаблонные функции обычно пересылают свои аргументы, поэтому, если у вас есть правильные перегрузки, принимающие ссылки rvalue, их следует вызывать. - person bipll; 21.08.2018