make_unique с инициализацией скобки - PullRequest
8 голосов
/ 13 марта 2019

https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique пишет, что std::make_unique может быть реализовано как

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

Это не работает для простых структур без конструкторов. Они могут быть инициализированы скобками, но не имеют конструктора не по умолчанию. Пример:

#include <memory>
struct point { int x, z; };
int main() { std::make_unique<point>(1, 2); }

При компиляции компилятор будет жаловаться на отсутствие конструктора с двумя аргументами, и это правильно.

Интересно, есть ли техническая причина не определять функцию в терминах инициализации скобок? Как в

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

То, что работает достаточно хорошо для сценария выше. Существуют ли другие законные варианты использования, которые могут сломаться?

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

Ответы [ 2 ]

10 голосов
/ 13 марта 2019

Некоторые классы имеют разное поведение с 2 стилями инициализации.например,

std::vector<int> v1(1, 2); // 1 element with value 2
std::vector<int> v2{1, 2}; // 2 elements with value 1 & 2

Может быть недостаточно причин для выбора одного предпочтения другого;Я думаю, что стандарт просто выберет один и сформулирует решение в явном виде.

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

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

В C ++ 20 это скомпилирует:

std::make_unique<point>(1, 2);

из-за нового правила , позволяющего инициализировать агрегаты из заключенного в скобки списка значений .

В C ++ 17 вы можете просто сделать:

std::unique_ptr<point>(new point{1, 2});

Это не будет работать с make_shared.Таким образом, вы также можете просто создать фабрику (переадресация влево в качестве упражнения):

template <typename... Args>
struct braced_init {
    braced_init(Args... args) : args(args...) { }
    std::tuple<Args...> args;

    template <typename T>
    operator T() const {
        return std::apply([](Args... args){
            return T{args...};
        }, args);
    }
};

std::make_unique<point>(braced_init(1, 2));

В C ++ 14 вам придется реализовать apply и написать функцию фабрики для braced_init, потому чтопока нет CTAD - но это выполнимо.

Видя, как общая тенденция предпочитает фигурные скобки для инициализации

Требуется цитирование.Это заряженная тема - но я определенно не согласен с претензией.

...