草庐IT

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

coder 2024-02-11 原文

具体来说:直接列表初始化(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/

有关c++ - std::make_shared/std::make_unique 不使用列表初始化有什么原因吗?的更多相关文章

  1. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

    这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

  2. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby-on-rails - rails : How to make a form post to another controller action - 2

    我知道您通常应该在Rails中使用新建/创建和编辑/更新之间的链接,但我有一个情况需要其他东西。无论如何我可以实现同样的连接吗?我有一个模型表单,我希望它发布数据(类似于新View如何发布到创建操作)。这是我的表格prohibitedthisjobfrombeingsaved: 最佳答案 使用:url选项。=form_for@job,:url=>company_path,:html=>{:method=>:post/:put} 关于ruby-on-rails-rails:Howtomak

  5. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  6. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  7. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  8. ruby - 在不使用 RVM 的情况下在 Mac 上卸载和升级 Ruby - 2

    我最近决定从我的系统中卸载RVM。在thispage提出的一些论点说服我:实际上,我的决定是,我根本不想担心Ruby的多个版本。我只想使用1.9.2-p290版本而不用担心其他任何事情。但是,当我在我的Mac上运行ruby--version时,它告诉我我的版本是1.8.7。我四处寻找如何简单地从我的Mac上卸载这个Ruby,但奇怪的是我没有找到任何东西。似乎唯一想卸载Ruby的人运行linux,而使用Mac的每个人都推荐RVM。如何从我的Mac上卸载Ruby1.8.7?我想升级到1.9.2-p290版本,并且我希望我的系统上只有一个版本。 最佳答案

  9. ruby-on-rails - 如何重构 "shared"方法? - 2

    我正在使用RubyonRails3.2.2,我想从我的模型/类中“提取”一些方法。也就是说,在不止一个类/模型中,我有一些方法(注意:方法与用户授权相关,并被命名为“CRUD方式”),这些方法实际上是相同的;所以我认为DRY方法是将这些方法放在“共享”模块或类似的东西中。实现该目标的常见且正确的方法是什么?例如,我应该将“共享”代码放在哪里(在哪些目录和文件中)?如何在我的类/模型中包含提到的方法?你有什么建议?注意:我正在寻找“RubyonRails制作东西的方式”。 最佳答案 一种流行的方法是使用ActiveSupport关注点

  10. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

随机推荐