如何在const与非const上下文复用C++类成员辅助函数
如何用单一实现支持const/非const上下文的字典节点访问?
问题描述
我封装了一个std::map的类,提供getter和setter操作值。为了避免重复代码,希望实现一个辅助函数get_private,既能在const对象上调用,也能在非const对象上调用,但不想维护两个重载版本。尝试写模板函数时遇到了模板参数推导失败的错误,同时受Misra/Autosar规则限制不能使用const_cast。
现有代码
class MyTest { public: void set(const std::string_view &name, const std::string_view &value) { auto *it = get_private(name); if(it != nullptr) { *it = value; } else { m_arr.insert({name, value}); } } std::string_view get(const std::string_view &name) const { auto *it = get_private(name); if(it != nullptr) { return *it; } return ""; } private: const std::string_view *get_private(const std::string_view &name) const { auto it = std::find_if(m_arr.begin(), m_arr.end(), [name](const auto &pair) -> bool { return pair.first == name; }); if(it == m_arr.end()) { return nullptr; } return &(it->second); } std::string_view *get_private(const std::string_view &name) { auto it = std::find_if(m_arr.begin(), m_arr.end(), [name](const auto &pair) -> bool { return pair.first == name; }); if(it == m_arr.end()) { return nullptr; } return &(it->second); } private: std::map<std::string_view, std::string_view> m_arr; };
使用示例
MyTest mt; mt.set("name1", "value1"); mt.set("name2", "value3"); std::string_view v = mt.get("name1"); std::cout << v << std::endl;
失败的模板尝试
我曾尝试编写以下模板函数,但编译器报错Candidate template ignored: couldn't infer template argument 'T':
template <typename T> typename std::conditional<std::is_same<T, std::string_view>::value, std::string_view, const std::string_view>::type *get_private(const std::string_view &name) { }
解决方案
可以通过模板函数复用核心查找逻辑,利用调用上下文的const属性推导返回类型,完全避免const_cast,符合Misra/Autosar规则。以下是两种可行方案:
方案1:基于this类型推导的模板函数
将核心逻辑放在模板函数中,通过std::conditional根据当前对象的const属性决定返回指针类型,外部只需要两个简单的重载触发模板推导:
class MyTest { public: void set(const std::string_view &name, const std::string_view &value) { auto *it = get_private(name); if(it != nullptr) { *it = value; } else { m_arr.insert({name, value}); } } std::string_view get(const std::string_view &name) const { auto *it = get_private(name); if(it != nullptr) { return *it; } return ""; } private: // 模板核心实现:根据Self的const属性决定返回类型 template <typename Self> auto get_private_impl(const std::string_view &name) -> typename std::conditional<std::is_const<Self>::value, const std::string_view*, std::string_view*>::type { auto it = std::find_if(m_arr.begin(), m_arr.end(), [name](const auto &pair) -> bool { return pair.first == name; }); if(it == m_arr.end()) { return nullptr; } return &(it->second); } // 非const重载:推导Self为MyTest std::string_view* get_private(const std::string_view &name) { return get_private_impl<MyTest>(name); } // const重载:推导Self为const MyTest const std::string_view* get_private(const std::string_view &name) const { return get_private_impl<const MyTest>(name); } private: std::map<std::string_view, std::string_view> m_arr; };
方案2:布尔模板参数区分上下文
用布尔模板参数标记是否为const上下文,将核心逻辑封装在模板实现中,外部重载调用对应版本:
class MyTest { public: void set(const std::string_view &name, const std::string_view &value) { auto *it = get_private(name); if(it != nullptr) { *it = value; } else { m_arr.insert({name, value}); } } std::string_view get(const std::string_view &name) const { auto *it = get_private(name); if(it != nullptr) { return *it; } return ""; } private: // 定义类型别名简化返回类型 template <bool IsConst> using ValuePtr = typename std::conditional<IsConst, const std::string_view*, std::string_view*>::type; // 模板核心实现:根据IsConst决定map的const属性 template <bool IsConst> ValuePtr<IsConst> get_private_impl(const std::string_view &name) const { using MapType = typename std::conditional<IsConst, const std::map<std::string_view, std::string_view>, std::map<std::string_view, std::string_view>>::type; MapType& map = m_arr; auto it = std::find_if(map.begin(), map.end(), [name](const auto &pair) -> bool { return pair.first == name; }); if(it == map.end()) { return nullptr; } return &(it->second); } // 非const调用:传入false std::string_view* get_private(const std::string_view &name) { return get_private_impl<false>(name); } // const调用:传入true const std::string_view* get_private(const std::string_view &name) const { return get_private_impl<true>(name); } private: std::map<std::string_view, std::string_view> m_arr; };
方案说明
- 两种方案都只需要维护一份核心查找逻辑,避免了代码重复。
- 完全遵守Misra/Autosar规则,未使用
const_cast进行类型转换。 - 编译器会根据调用上下文自动匹配对应的重载,返回正确的const/非const指针。
内容的提问来源于stack exchange,提问作者folibis
相关产品推荐
相关产品推荐

