为什么std::make_unique等函数没有std::initializer_list重载?

5
请参见Should I use () or {} when forwarding arguments?foo 是一个std::vector的克隆。

在N4140中,unique.ptr.create指定了std::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个回答

2
我不知道完整的历史,但最有可能的答案是“没有人提出过这个想法”。支持这一观点的证据是,std::unique_ptr在C++11中存在,而std::make_unique直到C++14才被添加。 std::make_shared(最终被复制到make_unique中)的代码和建议在initializer_list和支持它的初始化语法之前就已经存在(在boost中)。
现在你可以做的一件事是提出它(并解决任何边角情况,例如使用SFINAE删除重载,如果目标类型不支持initializer_list)。

我认为SFINAE并不是必要的。如果我将其从命名空间中取出并执行“using namespace std;”(以确保两个重载都可见),那么make_unique<std::pair<int, int>>(1, 73);可以完美地工作。如果有人执行make_unique<std::pair<int, int>>({1, 73});,那就是他们的问题了。 - user6319825
@user6319825。是的,SFINAE并不是必要的,无论如何编译都会失败,只是根据我的经验,在SFINAE环境中的错误信息更容易诊断。 - Niall
尽管如此,他们仍然故意使用new T(std::forward<Args>(args)...)而不是像对于optional一样由实现自行决定:“如果直接非列表初始化一个类型为T的对象,则使用参数std::forward <Args>(args)初始化包含的值....”这让我想到至少在make_unique方面有一些讨论。 - user6319825
有趣的是,optional 支持 initializer_list - Niall
@Niall 我发现重载函数对于实现完美转发非常有用。我想知道我是不是在早期的 optional 提案中学到了这个。 - Yakk - Adam Nevraumont
显示剩余2条评论

0

我认为这只是一个模板推导问题,请参见此答案

你可以尝试以下方法:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接