如何实现获取类中任意std::vector元素的通用索引访问函数?
问题描述
现有一个仅以各类std::vector作为数据成员的类S,希望实现一个通用函数,通过索引获取任意vector中的元素。我尝试用getter指针实现了功能,但觉得可读性不足,附上类S的示例代码和我的实现,寻求更优方案。
类S示例代码
class S { private: std::vector<int> someIntegers; std::vector<double> someDoubles; std::vector<double> someDoubles2; std::vector<int> someIntegers2; std::vector<B> someBs; public: const std::vector<int>& getSomeIntegers() const { return someIntegers; } const std::vector<double>& getSomeDoubles() const { return someDoubles; } const std::vector<double>& getSomeDoubles2() const { return someDoubles2; } const std::vector<int>& getSomeIntegers2() const { return someIntegers2; } const std::vector<B>& getSomeBs() const { return someBs; } // 其他getter... };
我的实现代码
template<typename T> std::optional<T> getByIndex(const S& s, const std::vector<T>& (S::*getCnt)() const, size_t index = 0) { if (const auto& cnt = (s.*getCnt)(); index >= 0 && index < cnt.size()) return cnt.at(index); return {}; }
优化方案
方案1:枚举+重载函数,提升可读性
定义枚举标识不同vector成员,针对每个枚举值重载getByIndex,调用对应getter:
enum class VectorType { Integers, Doubles, Doubles2, Integers2, Bs }; std::optional<int> getByIndex(const S& s, VectorType type, size_t index = 0) { switch(type) { case VectorType::Integers: { const auto& vec = s.getSomeIntegers(); return index < vec.size() ? std::optional<int>(vec.at(index)) : std::nullopt; } case VectorType::Integers2: { const auto& vec = s.getSomeIntegers2(); return index < vec.size() ? std::optional<int>(vec.at(index)) : std::nullopt; } default: return std::nullopt; } } std::optional<double> getByIndex(const S& s, VectorType type, size_t index = 0) { switch(type) { case VectorType::Doubles: { const auto& vec = s.getSomeDoubles(); return index < vec.size() ? std::optional<double>(vec.at(index)) : std::nullopt; } case VectorType::Doubles2: { const auto& vec = s.getSomeDoubles2(); return index < vec.size() ? std::optional<double>(vec.at(index)) : std::nullopt; } default: return std::nullopt; } } std::optional<B> getByIndex(const S& s, VectorType type, size_t index = 0) { if (type == VectorType::Bs) { const auto& vec = s.getSomeBs(); return index < vec.size() ? std::optional<B>(vec.at(index)) : std::nullopt; } return std::nullopt; }
调用示例:
auto val = getByIndex(s, VectorType::Integers, 2);
方案2:标签类封装类型信息
定义对应每个vector的标签类,通过模板特化实现函数:
struct IntegersTag {}; struct DoublesTag {}; struct Doubles2Tag {}; struct Integers2Tag {}; struct BsTag {}; template<typename Tag> std::optional<typename std::invoke_result_t<decltype(&S::getSomeIntegers), const S&>::value_type> getByIndex(const S& s, Tag, size_t index = 0); template<> std::optional<int> getByIndex(const S& s, IntegersTag, size_t index) { const auto& vec = s.getSomeIntegers(); return index < vec.size() ? std::optional<int>(vec.at(index)) : std::nullopt; } template<> std::optional<double> getByIndex(const S& s, DoublesTag, size_t index) { const auto& vec = s.getSomeDoubles(); return index < vec.size() ? std::optional<double>(vec.at(index)) : std::nullopt; } // 其他标签的特化实现...
调用示例:
auto val = getByIndex(s, IntegersTag{}, 1);
方案3:lambda封装getter,简化调用
用lambda包装getter,让调用意图更清晰,无需修改原有类:
template<typename Getter, typename T = typename std::invoke_result_t<Getter, const S&>::value_type> std::optional<T> getByIndex(const S& s, Getter getter, size_t index = 0) { const auto& vec = getter(s); return index < vec.size() ? std::optional<T>(vec.at(index)) : std::nullopt; }
调用示例:
auto val = getByIndex(s, [](const S& s) { return s.getSomeDoubles(); }, 3);
方案4:作为类S的成员函数
若允许修改类S,将通用函数作为成员函数,结合模板或重载:
class S { // 原有私有成员... public: // 原有getter... template<typename T> std::optional<T> getByIndex(const std::vector<T>& (S::*getter)() const, size_t index = 0) const { const auto& vec = (this->*getter)(); return index < vec.size() ? std::optional<T>(vec.at(index)) : std::nullopt; } // 可选:结合枚举的重载版本 enum class VectorType { Integers, Doubles, Doubles2, Integers2, Bs }; std::optional<int> getByIndex(VectorType type, size_t index = 0) const { switch(type) { case VectorType::Integers: { const auto& vec = getSomeIntegers(); return index < vec.size() ? std::optional<int>(vec.at(index)) : std::nullopt; } case VectorType::Integers2: { const auto& vec = getSomeIntegers2(); return index < vec.size() ? std::optional<int>(vec.at(index)) : std::nullopt; } default: return std::nullopt; } } };
调用示例:
S s; auto val = s.getByIndex(&S::getSomeIntegers, 0); // 或者用枚举版本 auto val2 = s.getByIndex(S::VectorType::Doubles, 1);
内容的提问来源于stack exchange,提问作者SteveJ
相关产品推荐
相关产品推荐

