用于存储多种类型值的 STL 容器?
STL Container for storing multiple types of values?
我有一个用于消息总线的 Message
结构,我想用消息发送数据。问题是数据的类型会有所不同;也许对于一条消息我只想发送一个整数,但对于另一条消息我想发送几个整数、一个字符串,甚至可能是一个指向对象的指针。我可以做这样的事情:
struct Message {
std::map<int, int> intPayload;
std::map<int, std::string> strPayload;
short id;
};
但这不仅丑陋而且不干净,而且可能会造成浪费 space,但如果我想传递一个相对奇特的数据类型,例如指向 class 例如。我应该为此使用什么?
一个使用继承和多态的简单例子:
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应该可以给你更多的信息。
您可以将您的解决方案基于访客模式。
作为一个最小的工作示例:
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
成员函数可以是虚拟的。
这样,您的所有侦听器都可以从 class 派生并覆盖所需的方法。
Bus
将接受一组 Listener
,无论实际的派生类型是什么,以及一个泛型 Message
。另一方面,message 会将自身提升为正确的派生类型,并将引用传递给给定的侦听器。
访问者模式背后的技术也称为双重调度,如果您想进一步探索它。
有很多方法可以做到这一点。这是一个 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]) );
vec1
是 int
或 std::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;
}
});
}
我有一个用于消息总线的 Message
结构,我想用消息发送数据。问题是数据的类型会有所不同;也许对于一条消息我只想发送一个整数,但对于另一条消息我想发送几个整数、一个字符串,甚至可能是一个指向对象的指针。我可以做这样的事情:
struct Message {
std::map<int, int> intPayload;
std::map<int, std::string> strPayload;
short id;
};
但这不仅丑陋而且不干净,而且可能会造成浪费 space,但如果我想传递一个相对奇特的数据类型,例如指向 class 例如。我应该为此使用什么?
一个使用继承和多态的简单例子:
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应该可以给你更多的信息。
您可以将您的解决方案基于访客模式。
作为一个最小的工作示例:
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
成员函数可以是虚拟的。
这样,您的所有侦听器都可以从 class 派生并覆盖所需的方法。
Bus
将接受一组 Listener
,无论实际的派生类型是什么,以及一个泛型 Message
。另一方面,message 会将自身提升为正确的派生类型,并将引用传递给给定的侦听器。
访问者模式背后的技术也称为双重调度,如果您想进一步探索它。
有很多方法可以做到这一点。这是一个 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]) );
vec1
是 int
或 std::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;
}
});
}