You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 16:24:19