Подтвердить что ты не робот

Вызов конструктора initializer_list через make_unique/make_shared

Я пытаюсь использовать std::make_unique, чтобы инициализировать класс, конструктор которого должен получить std::initializer_list. Здесь минимальный случай:

#include <string>
#include <vector>
#include <initializer_list>
#include <memory>

struct Foo {
    Foo(std::initializer_list<std::string> strings) : strings(strings) {}

    std::vector<std::string> strings;
};

int main(int, char**) {

    auto ptr = std::make_unique<Foo>({"Hello", "World"});

    return 0;
}

Вы можете видеть на Coliru, который он не создает:

main.cpp:14:56: error: no matching function for call to 'make_unique(<brace-enclosed initializer list>)'
     auto ptr = std::make_unique<Foo>({"Hello", "World"});

Итак, возможно ли make_unique использовать initializer_list? Есть ли ошибка в GCC 4.9.1? Или я что-то упустил?

4b9b3361

Ответ 1

std::make_unique - это шаблон функции, который выводит типы аргументов, которые передаются конструктору объекта. К сожалению, скопированные списки не выводятся (с исключением для объявлений auto), и поэтому вы не можете создать экземпляр шаблона функции при этом отсутствующем типе параметров.

Вы не можете использовать std::make_unique, но, пожалуйста, не ходите по этому маршруту – вы должны избегать голых new столько, сколько можете, ради детей. Или вы можете сделать работу типа вывода, указав тип:

  • std::make_unique<Foo>(std::initializer_list<std::string>({"Hello", "World"}))

  • std::make_unique<Foo, std::initializer_list<std::string>>({"Hello", "World"})

  • auto il = { "Hello"s, "World"s }; auto ptr = std::make_unique<Foo>(il);

Последняя опция использует специальное правило для объявлений auto, которые (как я намекнул выше) действительно выводят std::initializer_list.

Ответ 2

Если вы готовы ввести несколько дополнительных символов, вы можете сделать это:

auto ptr = std::make_unique<Foo>( make_init_list( { "Hello"s , "World"s } ));

где init_list определяется как

template<typename T>
std:: initializer_list<T> make_init_list ( std:: initializer_list<T> && l ) {
    return l;
}

Это позволяет сделать вывод и удобен, если в коде есть много мест, которые вы должны сделать.

(Работает на clang 3.9 и gcc 6.2.0. Я получил его для работы с g++ - 4.8.4, кроме того, что мне пришлось подстроить std::string -literal и изменить на make_shared. Но вывод T в пределах make_init_list работал нормально.)

Собственно, это это расширение? Успешная вычет для make_init_list, требуемая стандартом, или нет?