gpt4 book ai didi

c++ - std::make_shared/std::make_unique 不使用列表初始化有什么原因吗?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:23:18 27 4
gpt4 key购买 nike

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

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

根据我的经验,现实生活中的场景(例如在函数中声明临时结构)以有效地向 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
/// ...
}

在这里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)...));
}

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

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

这是另一个疏忽还是有一些理由支持这一选择。具体来说,列表初始化解决方案中可以有哪些陷阱? std::make_unique后来在 C++14 中引入,为什么它也遵循相同的模式?

最佳答案

Specifically, what pitfalls can be in the list initialization solution?

使用列表初始化的所有典型陷阱。

例如,非initializer_list构造函数的隐藏。什么是make_shared<vector<int>>(5, 2)做?如果你的答案是“构造一个由 5 个 int 组成的数组”,那是绝对正确的……只要 make_shared 没有使用列表初始化。因为那会在你做的那一刻改变。

请注意,突然更改它会破坏现有代码,因为现在所有间接初始化函数都使用构造函数语法。因此,您不能随心所欲地改变它并期望世界继续运转。

另外还有一个对这种情况来说更独特的问题:缩小问题:

struct Agg
{
char c;
int i;
};

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

您需要显式转换 5char .

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

why does it too follow same pattern?

它遵循相同的模式,因为拥有两个看起来几乎相同但具有完全不同的行为的不同功能并不是一件好事。


请注意,C++20 至少通过使构造函数样式的语法调用聚合初始化来解决此问题的聚合部分,如果初始化程序对于常规直接初始化而言是格式错误的。所以如果T是某种聚合类型(没有用户声明的构造函数),并且 T(args)不会调用复制/移动构造函数(唯一采用没有用户声明的构造函数的类型可能具有的参数的构造函数),然后这些参数将被用来尝试聚合初始化结构。

allocator::construct和其他形式的转发初始化默认为直接初始化,这将允许您通过转发初始化来初始化聚合。

如果不显式使用 initializer_list,您仍然无法执行其他列表初始化操作在调用站点。但这可能是最好的。

关于c++ - std::make_shared/std::make_unique 不使用列表初始化有什么原因吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41473882/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com