有没有理由说std :: make_shared/std :: make_unique不使用列表初始化?

Gre*_*ape 5 c++ std shared-ptr c++11

具体来说:直接列表初始化(cppreference.com (3)).

C++ 11中引入了两者std::make_shared和统一初始化功能.所以我们可以在堆上分配对象时使用聚合初始化:.这是直接初始化没有构造函数的对象的好方法,例如聚合,pod等.new Foo{1, "2", 3.0f}

根据我的经验,现实生活场景,例如在函数中声明临时结构,有效地为lambda提供一组参数变得非常普遍:

void foo()
{
    struct LambdaArgs
    {
        std::string arg1;
        std::string arg2;
        std::string arg3;
    };

    auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});

    auto lambda = [args] {
        /// ...
    };

    /// Use lambda
    /// ...
}
Run Code Online (Sandbox Code Playgroud)

这里auto args = std::make_shared<LambdaArgs>("1", "2", "3");应该很好但不会起作用,因为std::make_shared通常实现为:

template<typename T, typename... Args>
std::shared_ptr<T> make_shared(Args && ...args)
{
    return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
Run Code Online (Sandbox Code Playgroud)

所以我们坚持了auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});.

原本应该解决的问题std::make_shared仍然存在于没有构造函数的对象.而且解决方法不仅不美观而且效率低下.

这是另一种疏忽还是有一些理由来捍卫这种选择.具体来说,列表初始化解决方案中可能存在哪些陷阱?std::make_unique稍后介绍,在C++ 14中,为什么它也遵循相同的模式?

Nic*_*las 5

具体来说,列表初始化解决方案中可能存在哪些陷阱?

使用列表初始化的所有典型缺陷.

例如,隐藏非initializer_list构造函数.怎么make_shared<vector<int>>(5, 2)办?如果你的答案是"构造一个5 int秒的数组",这是绝对正确的...只要make_shared 不使用列表初始化.因为这会改变你做的那一刻.

请注意,突然改变这会破坏现有代码,因为现在所有间接初始化函数都使用构造函数语法.所以你不能随便改变它,并期望世界继续工作.

另外还有一个独特的案例:缩小的问题:

struct Agg
{
  char c;
  int i;
};
Run Code Online (Sandbox Code Playgroud)

您可以执行Agg a{5, 1020};初始化此聚合.但你永远做不到make_shared<Agg>(5, 1020).为什么?因为编译器可以保证文字5可以转换为a char而不会丢失数据.但是,当您使用这样的间接初始化时,文字5被模板推导为int.并且编译器不能保证任何int都可以转换为a char而不会丢失数据.这称为"缩小转换",在列表初始化中明确禁止.

您需要将其显式转换5为char.

标准库有一个问题:LWG 2089.虽然在技术上对这个问题的谈判allocator::construct,应该同样适用于所有间接初始化功能,如make_X和C++ 17的就地构造函数any/ optional/ variant.

为什么它也遵循相同的模式?

它遵循相同的模式,因为具有看起来几乎完全相同且具有根本和意外不同行为的两个不同功能将不是一件好事.