C++11中如何便捷创建含多种数据类型成员的简单对象?
Absolutely! In C++11, you have a few solid options to create a flexible, multi-type collection that works like Python's dictionaries—no need to write a custom struct with a constructor every single time. Let's break down the best approaches:
1. Use Boost.Variant (Most Convenient Option)
Boost is a staple in the C++ ecosystem, and boost::variant lets you store values of different types in a single container. Pair it with std::unordered_map and you get a near-perfect stand-in for Python dictionaries.
Here's how you'd implement your example:
#include <boost/variant.hpp> #include <unordered_map> #include <vector> #include <string> #include <iostream> // Define the set of types we need to support using VariantType = boost::variant<std::vector<int>, float, std::string>; int main() { // Create our multi-type collection std::unordered_map<std::string, VariantType> easy; easy["keyframe_range"] = std::vector<int>(); easy["value"] = 0.25f; easy["interpolation"] = std::string("bezier"); // Access values with boost::get (throws boost::bad_get if type mismatch) float val = boost::get<float>(easy["value"]); std::cout << "Value: " << val << std::endl; std::string interp = boost::get<std::string>(easy["interpolation"]); std::cout << "Interpolation: " << interp << std::endl; // For safe access without exceptions, use boost::get_if if (auto vec_ptr = boost::get<std::vector<int>>(&easy["keyframe_range"])) { // Use vec_ptr here } return 0; }
This approach lets you add new key-value pairs on the fly, no struct definitions required. Just make sure to include the Boost headers and link against the Boost library in your build setup.
2. Roll Your Own Tagged Union (No Third-Party Dependencies)
If you can't use Boost, you can implement a tagged union yourself. C++11 allows unions to hold non-POD types (like std::vector or std::string), though you'll need to manually manage construction and destruction to avoid undefined behavior.
Here's a simplified implementation:
#include <unordered_map> #include <vector> #include <string> #include <iostream> // Tag to track which type is stored in the union enum class TypeTag { None, IntVector, Float, String }; struct AnyValue { TypeTag tag; union { std::vector<int> int_vec; float f; std::string str; }; // Default constructor AnyValue() : tag(TypeTag::None) {} // Constructors for supported types AnyValue(std::vector<int> vec) : tag(TypeTag::IntVector) { // Placement new to construct the vector in the union's memory new (&int_vec) std::vector<int>(std::move(vec)); } AnyValue(float val) : tag(TypeTag::Float), f(val) {} AnyValue(std::string s) : tag(TypeTag::String) { new (&str) std::string(std::move(s)); } // Destructor: clean up non-POD types ~AnyValue() { switch (tag) { case TypeTag::IntVector: int_vec.~vector(); break; case TypeTag::String: str.~string(); break; default: break; } } // Assignment operator (simplified using copy-and-swap) AnyValue& operator=(AnyValue other) { swap(*this, other); return *this; } friend void swap(AnyValue& a, AnyValue& b) { using std::swap; swap(a.tag, b.tag); switch (a.tag) { case TypeTag::IntVector: swap(a.int_vec, b.int_vec); break; case TypeTag::Float: swap(a.f, b.f); break; case TypeTag::String: swap(a.str, b.str); break; default: break; } } // Copy/move constructors omitted for brevity - implement them properly for production code! }; int main() { std::unordered_map<std::string, AnyValue> easy; easy["keyframe_range"] = std::vector<int>(); easy["value"] = 0.25f; easy["interpolation"] = std::string("bezier"); // Access values by checking the tag first if (easy["value"].tag == TypeTag::Float) { std::cout << "Value: " << easy["value"].f << std::endl; } if (easy["interpolation"].tag == TypeTag::String) { std::cout << "Interpolation: " << easy["interpolation"].str << std::endl; } return 0; }
This gives you full control without external dependencies, but requires careful memory management. For production code, make sure to implement copy/move constructors properly to avoid leaks or crashes.
3. Use std::tuple (For Fixed-Type Temporary Objects)
If your temporary object has a fixed set of types (you just don't want to write a struct), std::tuple from C++11 is a lightweight option. It's less flexible than the above methods, but works great for one-off fixed collections:
#include <tuple> #include <vector> #include <string> #include <iostream> int main() { // Create a tuple matching your struct's types: vector<int>, float, string auto easy = std::make_tuple(std::vector<int>(), 0.25f, std::string("bezier")); // Access elements via index or type (if types are unique) float val = std::get<1>(easy); std::cout << "Value: " << val << std::endl; std::string interp = std::get<std::string>(easy); std::cout << "Interpolation: " << interp << std::endl; return 0; }
The downside here is you can't dynamically add new elements, and accessing them requires remembering their index or unique type. It's best for simple, fixed-use cases.
内容的提问来源于stack exchange,提问作者EllipticalInitial

