C++ 基于variant向量的类实现范围for循环方法
实现方案说明
C++是静态类型语言,函数返回值、变量类型必须在编译期确定。由于你设计的Signal是运行时确定存储类型的非模板类,begin()/end()必须返回统一静态类型的迭代器——三种std::vector的迭代器是完全不同的类型,无法直接作为同一函数的返回值,因此你之前尝试的直接返回原生vector迭代器的方案不可行。
你在补充说明中期望auto& item直接推导出uint8_t&/float&的效果无法直接实现:auto是纯编译期推导关键字,无法感知运行时才确定的元素类型。可行的实现方式是自定义类型擦除迭代器,对外提供统一迭代接口,迭代器解引用返回持有实际元素引用的std::variant对象,修改该对象会直接修改Signal内部存储的真实数据,完全满足遍历修改的需求。
完整实现代码
本方案基于C++17及以上标准(和你使用std::variant的版本要求一致):
#include <variant> #include <vector> #include <cstdint> #include <memory> #include <stdexcept> #include <iterator> enum class Type { UINT8, INT16, FLOAT }; using VariantVector = std::variant<std::vector<uint8_t>, std::vector<int16_t>, std::vector<float>>; using ElementRef = std::variant<uint8_t&, int16_t&, float&>; using ConstElementRef = std::variant<const uint8_t&, const int16_t&, const float&>; // 非const遍历迭代器 class SignalIterator { private: struct IteratorBase { virtual ~IteratorBase() = default; virtual void increment() = 0; virtual ElementRef deref() const = 0; virtual bool equal(const IteratorBase& other) const = 0; virtual std::unique_ptr<IteratorBase> clone() const = 0; }; template <typename VecIt> class IteratorImpl final : public IteratorBase { public: explicit IteratorImpl(VecIt it) : m_it(std::move(it)) {} void increment() override { ++m_it; } ElementRef deref() const override { return ElementRef(*m_it); } bool equal(const IteratorBase& other) const override { const auto* casted = dynamic_cast<const IteratorImpl*>(&other); return casted && m_it == casted->m_it; } std::unique_ptr<IteratorBase> clone() const override { return std::make_unique<IteratorImpl>(*this); } private: VecIt m_it; }; std::unique_ptr<IteratorBase> m_impl; public: template <typename VecIt> SignalIterator(VecIt it) : m_impl(std::make_unique<IteratorImpl<VecIt>>(std::move(it))) {} SignalIterator(const SignalIterator& other) : m_impl(other.m_impl->clone()) {} SignalIterator& operator=(const SignalIterator& other) { if (this != &other) m_impl = other.m_impl->clone(); return *this; } SignalIterator(SignalIterator&&) noexcept = default; SignalIterator& operator=(SignalIterator&&) noexcept = default; SignalIterator& operator++() { m_impl->increment(); return *this; } SignalIterator operator++(int) { SignalIterator tmp = *this; ++(*this); return tmp; } ElementRef operator*() const { return m_impl->deref(); } bool operator==(const SignalIterator& other) const { return m_impl->equal(*other.m_impl); } bool operator!=(const SignalIterator& other) const { return !(*this == other); } using value_type = std::variant<uint8_t, int16_t, float>; using reference = ElementRef; using pointer = void; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; }; // const遍历迭代器 class ConstSignalIterator { private: struct IteratorBase { virtual ~IteratorBase() = default; virtual void increment() = 0; virtual ConstElementRef deref() const = 0; virtual bool equal(const IteratorBase& other) const = 0; virtual std::unique_ptr<IteratorBase> clone() const = 0; }; template <typename VecIt> class IteratorImpl final : public IteratorBase { public: explicit IteratorImpl(VecIt it) : m_it(std::move(it)) {} void increment() override { ++m_it; } ConstElementRef deref() const override { return ConstElementRef(*m_it); } bool equal(const IteratorBase& other) const override { const auto* casted = dynamic_cast<const IteratorImpl*>(&other); return casted && m_it == casted->m_it; } std::unique_ptr<IteratorBase> clone() const override { return std::make_unique<IteratorImpl>(*this); } private: VecIt m_it; }; std::unique_ptr<IteratorBase> m_impl; public: template <typename VecIt> ConstSignalIterator(VecIt it) : m_impl(std::make_unique<IteratorImpl<VecIt>>(std::move(it))) {} ConstSignalIterator(const ConstSignalIterator& other) : m_impl(other.m_impl->clone()) {} ConstSignalIterator& operator=(const ConstSignalIterator& other) { if (this != &other) m_impl = other.m_impl->clone(); return *this; } ConstSignalIterator(ConstSignalIterator&&) noexcept = default; ConstSignalIterator& operator=(ConstSignalIterator&&) noexcept = default; ConstSignalIterator& operator++() { m_impl->increment(); return *this; } ConstSignalIterator operator++(int) { ConstSignalIterator tmp = *this; ++(*this); return tmp; } ConstElementRef operator*() const { return m_impl->deref(); } bool operator==(const ConstSignalIterator& other) const { return m_impl->equal(*other.m_impl); } bool operator!=(const ConstSignalIterator& other) const { return !(*this == other); } using value_type = std::variant<uint8_t, int16_t, float>; using reference = ConstElementRef; using pointer = void; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; }; class Signal { public: explicit Signal(size_t size, Type type, double value) : m_size(size), m_type(type) { switch(type) { case Type::UINT8: m_data = std::vector<uint8_t>(size, static_cast<uint8_t>(value)); break; case Type::INT16: m_data = std::vector<int16_t>(size, static_cast<int16_t>(value)); break; case Type::FLOAT: m_data = std::vector<float>(size, static_cast<float>(value)); break; default: throw std::invalid_argument("Invalid signal type"); } } SignalIterator begin() { return std::visit([](auto&& vec) { return SignalIterator(vec.begin()); }, m_data); } SignalIterator end() { return std::visit([](auto&& vec) { return SignalIterator(vec.end()); }, m_data); } ConstSignalIterator begin() const { return std::visit([](auto&& vec) { return ConstSignalIterator(vec.cbegin()); }, m_data); } ConstSignalIterator end() const { return std::visit([](auto&& vec) { return ConstSignalIterator(vec.cend()); }, m_data); } ConstSignalIterator cbegin() const { return begin(); } ConstSignalIterator cend() const { return end(); } // 其他自定义方法... private: size_t m_size; Type m_type; VariantVector m_data; };
使用方式
遍历时通过std::visit访问实际元素,修改操作会直接作用于Signal内部存储的数据:
#include <iostream> int main() { Signal uint_signal(5, Type::UINT8, 10); for (auto&& item : uint_signal) { std::visit([](auto&& val) { using ValT = std::decay_t<decltype(val)>; if constexpr (std::is_same_v<ValT, uint8_t>) { val += 2; // 直接修改内部存储的uint8_t元素 std::cout << "uint8_t value: " << static_cast<int>(val) << "\n"; } }, item); } Signal float_signal(3, Type::FLOAT, 1.5f); for (auto&& item : float_signal) { std::visit([](auto&& val) { using ValT = std::decay_t<decltype(val)>; if constexpr (std::is_same_v<ValT, float>) { val *= 3; // 直接修改内部存储的float元素 std::cout << "float value: " << val << "\n"; } }, item); } return 0; }
可选优化方案
如果对遍历性能要求更高,完全不需要自定义类型擦除迭代器,可以直接根据运行时的类型枚举分支,取出对应类型的vector后再遍历,这种方式没有类型擦除开销,只是写法上稍显冗余:
// 为Signal添加public方法获取类型和底层vector即可 Signal s(100, Type::INT16, 5); switch(s.get_type()) { case Type::UINT8: { auto& vec = std::get<std::vector<uint8_t>>(s.get_data()); for (auto& item : vec) { // item为uint8_t&,可直接操作 } break; } case Type::INT16: { auto& vec = std::get<std::vector<int16_t>>(s.get_data()); for (auto& item : vec) { // item为int16_t&,可直接操作 } break; } case Type::FLOAT: { auto& vec = std::get<std::vector<float>>(s.get_data()); for (auto& item : vec) { // item为float&,可直接操作 } break; } }
内容的提问来源于stack exchange,提问作者user2229053
相关产品推荐
相关产品推荐

