【问题标题】:Pass a list of derived types in order to store them as a member传递派生类型列表以便将它们存储为成员
【发布时间】:2016-10-28 08:25:39
【问题描述】:

我有一堂课widget
我有一个抽象类 base 与派生 derived_aderived_b 等。

我希望widget 保存从base 派生的任意数量的对象,以便以后多态地使用它们。

我的第一次尝试是这样的:

#include <vector>
#include <ostream>
#include <iostream>
#include <memory>

class widget {
public:
    explicit widget(std::vector<std::unique_ptr<base>>&& params) :
    members {std::move (params)}
    {            
    }

private:
    std::vector<std::unique_ptr<base>> members;
};

会这样称呼:

std::vector<std::unique_ptr<base>> v;
v.push_back(std::move(std::make_unique<derived_a>()));
widget w (std::move(v));

但是,此解决方案过于冗长且对用户不友好,尤其是在提供多种类型时:

std::vector<std::unique_ptr<base>> v;
v.push_back(std::move(std::make_unique<derived_a>()));
v.push_back(std::move(std::make_unique<derived_b>()));
v.push_back(std::move(std::make_unique<derived_c>()));
v.push_back(std::move(std::make_unique<derived_a>()));
v.push_back(std::move(std::make_unique<derived_b>()));
v.push_back(std::move(std::make_unique<derived_c>()));
widget w {std::move(v)};

相反,我更喜欢使用

widget w {derived_a(), 
          derived_b(), 
          derived_c(), 
          derived_a(), 
          derived_b(), 
          derived_c()};

以便widget 提供一个右值列表,然后它可以将其转换为std::vector&lt;unique_ptr&lt;base&gt;&gt;
我的印象是,这可以通过模板化 ctor 来实现,但是,尽管进行了密集的谷歌搜索,但我不知道如何准确地实现我的目标。

请注意,类模板解决方案看起来像这样:

widget<derived_a, 
       derived_b, 
       derived_c, 
       derived_a, 
       derived_b, 
       derived_c> w;

不受欢迎,因为我需要为一些派生提供参数。

【问题讨论】:

  • 抱歉 - 我有一段时间没有考虑 OOP 问题...但是您不能通过使用工厂类来实现一些更简单的编码吗? class wf { static std::unique_ptr&lt;derived_a&gt; make_a(/*maybe some args*/) { ... }; ... }; 然后widget w; w.visuals( { wf::make_a(); ... } )?
  • 只是为了“少”一点冗长。 AFAIK 你不需要push_back 中的move
  • 很好,我的工厂想法失败了。 std::unique&lt;&gt; 似乎在处理抽象基类时遇到了问题。谁会猜到他们在内部尝试复制? :)
  • @Hayt 是的,std::move 在使书面代码臃肿的同时一无所获。

标签: c++ c++11 templates polymorphism c++14


【解决方案1】:

很遗憾,您不能使用initializer_list,但可以使用可变参数模板:

class widget {
public:
    template <typename ... Ts>
    explicit widget(Ts&&... params) 
    {
        int dummy[] =
            {0, (members.emplace_back(std::make_unique<Ts>(std::forward<Ts>(params))), 0)...};
        static_cast<void>(dummy); // avoid unused variable warning
    }

private:
    std::vector<std::unique_ptr<base>> members;
};

Demo

或在 C++17 中,带有折叠表达式:

    template <typename ... Ts>
    explicit widget(Ts&&... params) 
    {
        (members.emplace_back(std::make_unique<Ts>(std::forward<Ts>(params))), ...);
    }

【讨论】:

  • 我宁愿使用std::forward,而不是std::move
  • @themagicalyang 这样做在 MSVC 中给了我'std::forward': no matching overloaded function found,而带有std::move 的版本完美运行
  • @JanNilsFerner 它适用于 clang 3.8 和 gcc 6.1 coliru.stacked-crooked.com/a/31d9cc1434505b55
  • @JanNilsFerner 这是逗号运算符。 ... 扩展为逗号运算符的用法。
  • @JanNilsFerner :“似乎是另一个微软特有的错误。”VC++ 可以很好地编译这段代码。我敢打赌你把它从 std::move( 改为 std::forward( 而不是正确的 std::forward&lt;Ts&gt;(
猜你喜欢
  • 2016-05-21
  • 1970-01-01
  • 2022-10-14
  • 2012-02-07
  • 2017-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-16
相关资源
最近更新 更多