如何从字符串推导C++模板get函数的返回类型?
实现自动推导返回类型的模板get函数
核心限制说明
首先明确:如果get的参数是运行时动态生成的字符串,无法实现自动推导返回类型——因为C++函数的返回类型必须在编译期确定,而运行时字符串的值编译器无法预知。这种场景下要么显式指定模板参数,要么返回std::variant这类可容纳多类型的容器。
但如果你的调用场景中,get的参数是编译期已知的字符串字面量,完全可以实现类似cfg.get("v1")(或略作语法调整)的自动推导调用。
方案1:C++20 字符串字面量非类型模板参数(推荐)
利用C++20支持的std::string_view非类型模板参数,直接将字符串作为模板参数传入,通过if constexpr在编译期分支处理,自动推导返回类型:
原有Legacy代码结构
// 不可修改的Legacy代码 struct Data { unsigned int v1; bool f1; }; class LegacyCfg { public: unsigned int get_uint(const std::string& key) { if (key == "v1") return data.v1; throw std::invalid_argument("invalid uint key"); } bool get_bool(const std::string& key) { if (key == "f1") return data.f1; throw std::invalid_argument("invalid bool key"); } private: Data data; };
Wrapper实现
#include <stdexcept> #include <string_view> class CfgWrapper { public: // 字符串字面量作为非类型模板参数 template<std::string_view Key> auto get() { if constexpr (Key == "v1"sv) { return legacy_cfg.get_uint("v1"); } else if constexpr (Key == "f1"sv) { return legacy_cfg.get_bool("f1"); } else { static_assert(Key == ""sv, "Unsupported key: 仅支持v1/f1"); throw std::invalid_argument("Unsupported key"); } } explicit CfgWrapper(LegacyCfg cfg) : legacy_cfg(std::move(cfg)) {} private: LegacyCfg legacy_cfg; };
调用方式
int main() { LegacyCfg legacy; CfgWrapper cfg(legacy); // 自动推导返回类型为unsigned int auto v1_val = cfg.get<"v1">(); // 自动推导返回类型为bool auto f1_val = cfg.get<"f1">(); }
方案2:C++17 兼容的编译期字符串判断
如果无法使用C20,可以利用C17的constexpr字符串比较,让编译器在编译期确定分支,从而推导返回类型:
#include <stdexcept> #include <string_view> class CfgWrapper { public: // 仅支持编译期常量字符串参数 template<typename Str> auto get(const Str& key) { if constexpr (std::is_same_v<Str, std::string_view>) { if (key == "v1"sv) return legacy_cfg.get_uint("v1"); else if (key == "f1"sv) return legacy_cfg.get_bool("f1"); } else if constexpr (std::is_same_v<Str, const char*>) { if (std::string_view(key) == "v1"sv) return legacy_cfg.get_uint("v1"); else if (std::string_view(key) == "f1"sv) return legacy_cfg.get_bool("f1"); } throw std::invalid_argument("Unsupported key"); } explicit CfgWrapper(LegacyCfg cfg) : legacy_cfg(std::move(cfg)) {} private: LegacyCfg legacy_cfg; };
调用方式
int main() { LegacyCfg legacy; CfgWrapper cfg(legacy); // 传入字符串字面量,编译期推导返回类型 auto v1_val = cfg.get("v1"); auto f1_val = cfg.get("f1"sv); // 用string_view更明确 }
注意:此方案下如果传入运行时字符串(比如std::string key = "v1"; cfg.get(key);),编译会失败,因为编译器无法确定返回类型。
运行时字符串的替代方案
如果必须支持运行时动态字符串,只能返回std::variant类型,由调用方自行提取对应类型:
#include <variant> #include <stdexcept> class CfgWrapper { public: std::variant<unsigned int, bool> get(const std::string& key) { if (key == "v1") return legacy_cfg.get_uint("v1"); else if (key == "f1") return legacy_cfg.get_bool("f1"); throw std::invalid_argument("Unsupported key"); } explicit CfgWrapper(LegacyCfg cfg) : legacy_cfg(std::move(cfg)) {} private: LegacyCfg legacy_cfg; };
调用方式
int main() { LegacyCfg legacy; CfgWrapper cfg(legacy); std::string runtime_key = "v1"; auto val = cfg.get(runtime_key); // 提取对应类型,类型不匹配会抛出std::bad_variant_access unsigned int v1_val = std::get<unsigned int>(val); }
内容的提问来源于stack exchange,提问作者limo
相关产品推荐
相关产品推荐

