2016-05-11 11 views
5

См. Should I use() or {} when forwarding arguments?. foo - это клон std::vector.Почему не существует перегрузки std :: initializer_list для std :: make_unique et al?

В N4140, unique.ptr.createstd::make_unique определяется как так:

template <class T, class... Args> unique_ptr<T> make_unique(Args&&... args);

  • Замечания: Эта функция не будет участвовать в разрешении перегрузки, если T не является массивом.

  • : unique_ptr<T>(new T(std::forward<Args>(args)...)).

Это означает, что реализации должны использовать (), а не {} для инициализации объектов. В качестве примера, следующие

auto s1 = std::make_unique<foo>(3, 1).get()->size(); 
auto s2 = std::make_unique<foo>(1).get()->size(); 
auto s3 = std::make_unique<foo>(2).get()->size(); 
std::cout << s1 << s2 << s3; 

выходы 312 тогда как если {} (внутри std::make_unique) был использован 211 бы выход. Поскольку списки инициализаторов не могут быть выведены, std::initializer_list должен быть явно передан для получения последнего результата. Вопрос в том, почему не такая перегрузка, как это предусмотрено?

namespace test 
{ 

template <class T, class Deduce> 
std::unique_ptr<T> make_unique(std::initializer_list<Deduce> li) 
{ 
    return ::std::make_unique<T>(li); 
} 

}; 

int main() 
{ 
    auto p1 = test::make_unique<foo>({3, 1}).get()->size(); 
    auto p2 = test::make_unique<foo>({1}).get()->size(); 
    auto p3 = test::make_unique<foo>({2}).get()->size(); 
    std::cout << p1 << p2 << p3; 
} 

Выходы 211.

Я не считаю причины «вы можете написать сами» или «во избежание раздувания стандарта», чтобы быть очень вескими причинами. Есть ли недостатки в обеспечении этой перегрузки?

ответ

2

Я не знаю полной истории, но наиболее вероятный ответ «никто не предложил».

Доказательства этого подтверждают тот факт, что std::make_unique был добавлен только в C++ 14, но std::unique_ptr существует в C++ 11.

Код и предложения std::make_shared (который был в конечном счете отражается в make_unique) существовали (в наддуве) до initializer_list и синтаксиса инициализации, поддерживающего его.

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

+0

Я не думаю, что SFINAE необходимо. 'make_unique > (1, 73);' отлично работает, если я вывожу его из пространства имен и выполняю 'using namespace std;' (чтобы убедиться, что обе перегрузки видны). Если кто-то делает make_unique > ({1, 73}), это их вина. – user6319825

+0

@ пользователь6319825.Да, SFINAE не является существенным, компиляция терпит неудачу в любом случае, как раз по моему опыту, сообщения об ошибках в среде SFINAE «легче» диагностировать. – Niall

+0

Еще. Они преднамеренно обсуждают «новый T (std :: forward (args) ...)», а не оставляют его до реализации, например [необязательно] (http://eel.is/c++draft/optional.object .ctor # 23): «Инициализирует содержащееся значение, как будто direct-non-list-initializing объект типа' T' с аргументами 'std :: forward (args)' .... »Это заставляет меня думать, что есть некоторое обсуждение * где-то * по крайней мере для 'make_unique' конкретно. – user6319825

0

Я думаю, что это просто вопрос о вычете шаблона, см. this answer.

Вы можете попробовать это:

int main() 
{ 
    auto p1 = std::make_unique<foo>(std::initializer_list<int>{3, 1}).get()->size(); 
    auto p2 = std::make_unique<foo>(foo{1}).get()->size(); 
    auto l3 = {2}; 
    auto p3 = std::make_unique<foo>(l3).get()->size(); 
    std::cout << p1 << p2 << p3; 
}