【问题标题】:STL Container for storing multiple types of values?用于存储多种类型值的 STL 容器?
【发布时间】:2017-05-30 05:37:46
【问题描述】:

我有一个与消息总线一起使用的Message 结构,我想用消息发送数据。问题是数据的类型会有所不同;也许对于一条消息,我只想发送一个 int,但对于另一条消息,我想发送几个 int、一个字符串,甚至可能是一个指向对象的指针。我可以做这样的事情:

struct Message {
    std::map<int, int> intPayload;
    std::map<int, std::string> strPayload;
    short id;
};

但这不仅丑陋且不干净,而且可能会浪费空间,而且如果我想传递一个相对奇特的数据类型,例如指向类实例的指针,这并不能解释。我应该为此使用什么?

【问题讨论】:

  • 使用继承和指向基类和虚函数的指针?
  • 如果可能的话,我的第一个猜测是多态性。
  • @DeiDei 我有点困惑,根据我的理解,多态性与类/结构继承有关,多态性在这里有什么帮助?
  • 或者你们两个都建议我创建一个Message 结构、一个MessageString 结构和一个MessageStringNoInt 结构等等??
  • 如果你知道所有可能的类型,std::variantboost::variant 通常是合适的。

标签: c++ stl containers std message-bus


【解决方案1】:

一个使用继承和多态的简单例子:

struct MessageBase
{
    // The function to send *this* message to the receiver
    virtual void send(ReceiverClass*) = 0;
};

struct MessageInt : MessageBase
{
    int payload;

    void send(ReceiverClass* receiver)
    {
        // Code to send this message type to the receiver...
    }
};

struct MessageString : MessageBase
{
    std::string payload;

    void send(ReceiverClass* receiver)
    {
        // Code to send this message type to the receiver...
    }
};

// ...

// Vector to store the messages
std::vector<MessageBase*> messages;

// Add a couple of messages
messages.push_back(new MessageInt{123});
messages.push_back(new MessageString{"Foobar"});

// Send the message to some receiver
for (auto const* message : messages)
    message->send(some_reciver_object);

任何good book 都应该能够为您提供更多信息。

【讨论】:

    【解决方案2】:

    您可以将解决方案基于访问者模式。
    作为一个最小的工作示例:

    struct Listener;
    
    struct Message {
        virtual void accept(Listener &) = 0;
    };
    
    struct SimpleMessage: Message {
        void accept(Listener &) override;
        int i;
    };
    
    struct ComplexMessage: Message {
        void accept(Listener &) override;
        int i;
        char c;
        double d;
    };
    
    struct Listener {
        void visit(SimpleMessage &) {}
        void visit(ComplexMessage &) {}
        void listen(Message &m) { m.accept(*this); }
    };
    
    void SimpleMessage::accept(Listener &l) { l.visit(*this); }
    void ComplexMessage::accept(Listener &l) { l.visit(*this); }
    
    struct Bus {
        Bus(Listener *l): l{l} {}
        void publish(Message &m) { l->listen(m); }
    private:
        Listener *l;
    };
    
    int main() {
        Listener l;
        Bus b{&l};
    
        SimpleMessage sm;
        ComplexMessage cm;
    
        b.publish(sm);
        b.publish(cm);
    }
    

    抛开Bus 的实现很简单这一事实,请注意Listener 中的visit 成员函数可以是虚拟的。
    这样,您的所有侦听器都可以从该类派生并覆盖所需的方法。
    Bus 将接受一组Listeners,无论实际派生类型是什么,以及一个通用Message。另一方面,message 会将自身提升为正确的派生类型,并将引用传递给给定的侦听器。

    访问者模式背后的技术也称为双重调度,如果您想进一步探索的话。

    【讨论】:

      【解决方案3】:

      有很多方法可以做到这一点。这是 C++17 的 std::variant 示例:

      std::vector<std::variant<int, std::string>> vec1;
      
      vec1.emplace_back(1);
      vec1.emplace_back("hello"s);
      
      doSomethingWithInt( std::get<int>(vec1[0]) );
      doSomethingWithString( std::get<std::string>(vec1[1]) );
      

      vec1intstd::string 的元素列表。

      您也可以使用静态访问者:

      std::vector<std::variant<int, std::string>> vec2;
      
      // ...
      
      for(auto&& variant : vec1) {
          variant.visit([](auto value){
              using t = decltype(value);
      
              if constexpr (std::is_same_v<t, int>) {
                  std::cout << "value is a int!" << std::endl;
              } else if constexpr (std::is_same_v<t, std::string>) {
                  std::cout << "value is a string!" << std::endl;
              }
          });
      }
      

      【讨论】:

      • 我怀疑在他的情况下,他不能说出从处理消息的类中的变体中得到的类型是什么。
      • 这在 Visual Studio 2015 中不起作用(我不能包含 &lt;variant&gt;),我做错了什么吗?它只是不支持 C++17 吗?
      • 有很多方法可以知道它是哪种类型。最好的方法是通过方法访问、get_if 和get。文档相当完整。去看看cppreference上的std::variant页面
      • @Omega msvc 还不完全支持 c++11(缺少表达式 sfinae)。 VS 2015 也没有完成他们的 c++14。如果你想要现代 C++,你将需要一个现代编译器。 msvc 团队正在努力做到这一点,但还没有完成。 VS 2017 在 C++11/14/17 方面做得更好。
      • 我必须承认我喜欢你的解决方案。也就是说,C++17 在现代编译器上还不是现实(仍然是工作草案)。如果他绑定到 MSVC,他将能够在 2027 年使用if constexpr(部分实现或带有花哨的错误)。 :-)
      猜你喜欢
      • 2018-08-16
      • 1970-01-01
      • 1970-01-01
      • 2013-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多