C++命令行参数库:模板setter设值后,能否实现非模板getter?
解决方案:实现无需显式指定类型的
get()接口 你的核心需求是让get()自动推导返回类型,这在C++中需要解决运行时类型信息与编译期类型推导的矛盾——因为std::string键是运行时确定的,编译器无法直接从字符串映射到类型。下面提供两种实用的优化方案:
方案1:使用代理类延迟类型转换
通过返回一个代理对象,让类型转换在使用时(赋值或输出)自动完成,无需显式指定类型。
实现代码
#include <iostream> #include <unordered_map> #include <any> #include <string> class Arguments { private: std::unordered_map<std::string, std::any> Values; // 代理类:负责自动类型转换和输出 class ValueProxy { private: std::any& m_value; public: explicit ValueProxy(std::any& val) : m_value(val) {} // 自动转换为目标类型 template<typename TargetType> operator TargetType&() { return std::any_cast<TargetType&>(m_value); } // 支持输出流 friend std::ostream& operator<<(std::ostream& os, const ValueProxy& proxy) { // 可扩展此处支持更多类型 if (proxy.m_value.type() == typeid(int)) { os << std::any_cast<int>(proxy.m_value); } else if (proxy.m_value.type() == typeid(std::string)) { os << std::any_cast<std::string>(proxy.m_value); } else if (proxy.m_value.type() == typeid(double)) { os << std::any_cast<double>(proxy.m_value); } return os; } }; public: // 添加参数并返回类型化引用,支持直接赋值 template<typename ValueType> ValueType& add(const std::string& key) { Values.emplace(key, ValueType{}); return std::any_cast<ValueType&>(Values[key]); } // 返回代理对象,无需显式指定类型 ValueProxy get(const std::string& key) { return ValueProxy(Values.at(key)); } }; int main() { Arguments args; // 添加参数并直接赋值 args.add<int>("count") = 100; args.add<std::string>("name") = "cli-tool"; args.add<double>("ratio") = 0.75; // 直接输出,无需指定类型 std::cout << "Count: " << args.get("count") << "\n"; std::cout << "Name: " << args.get("name") << "\n"; std::cout << "Ratio: " << args.get("ratio") << "\n"; // 自动转换为变量类型 int cnt = args.get("count"); std::string name = args.get("name"); double ratio = args.get("ratio"); return 0; }
优势
- 接口简洁:
get(key)直接使用,无需显式模板参数 - 支持赋值和输出两种常用场景的自动类型转换
- 兼容原有
add<Type>(key)的使用方式
方案2:编译期类型绑定(C++20+)
如果可以接受编译期常量键(比如字符串字面量),可以通过模板参数将键与类型绑定,让编译器在编译期推导返回类型。
实现代码
#include <iostream> #include <unordered_map> #include <any> #include <string_view> #include <typeindex> #include <stdexcept> // 编译期键标记 template<std::string_view Key> struct TypedKey {}; class Arguments { private: std::unordered_map<std::string, std::any> Values; // 存储键对应的类型信息,用于安全检查 std::unordered_map<std::string, std::type_index> TypeMap; public: // 编译期键绑定类型 template<typename ValueType, std::string_view Key> void add() { const std::string key_str{Key}; Values.emplace(key_str, ValueType{}); TypeMap.emplace(key_str, typeid(ValueType)); } // 编译期推导返回类型 template<std::string_view Key> auto get() { using ValueType = std::decay_t<decltype(std::any_cast<int>(Values.at(std::string{Key})))>; const std::string key_str{Key}; // 安全检查:确保类型匹配 if (TypeMap.at(key_str) != typeid(ValueType)) { throw std::bad_any_cast{}; } return std::any_cast<ValueType>(Values.at(key_str)); } }; int main() { Arguments args; // 绑定编译期键与类型 args.add<int, "count">(); args.add<std::string, "name">(); // 直接获取,编译器自动推导返回类型 std::cout << "Count: " << args.get<"count">() << "\n"; std::cout << "Name: " << args.get<"name">() << "\n"; int cnt = args.get<"count">(); return 0; }
优势
- 完全类型安全:编译期检查类型匹配,避免运行时
bad_any_cast - 无需显式指定返回类型,编译器自动推导
关键原理说明
C++函数的返回类型必须在编译期确定,因此直接让get(std::string)返回任意类型是不可能的。上述方案通过两种方式绕过这个限制:
- 代理类将类型转换延迟到使用时(赋值/输出),利用运算符重载实现自动转换
- 编译期键将类型信息嵌入模板参数,让编译器在编译期确定返回类型
内容的提问来源于stack exchange,提问作者Oxyn
相关产品推荐
相关产品推荐

