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

如何避免Vector与VectorView类的代码冗余并实现兼容的运算操作?

如何避免Vector与VectorView类的代码冗余并实现兼容的运算操作?

这个问题我之前做几何工具库时也踩过坑——重复写一堆几乎一模一样的运算符和方法,不仅繁琐还容易出错!其实咱们可以用几个C++模板技巧,彻底解决代码冗余,同时让Vector和VectorView的运算无缝兼容。

第一步:用CRTP提取公共逻辑

核心思路是把两个类的公共操作(比如operator+=、点积、标量运算等)抽出来,放到一个基于CRTP(奇异递归模板模式)的基类里。基类通过子类提供的data()方法统一访问数据,不用关心子类是自己持有数组还是指向外部指针。

先写基类:

template <typename Derived, typename T, std::size_t N>
class VectorBase {
public:
    // 统一数据访问接口,由子类实现
    T* data() { return static_cast<Derived*>(this)->data(); }
    const T* data() const { return static_cast<const Derived*>(this)->data(); }

    // 公共的operator+=实现,一次编写,子类共享
    Derived& operator+=(const VectorBase& other) {
        for (std::size_t i = 0; i < N; ++i) {
            data()[i] += other.data()[i];
        }
        return *static_cast<Derived*>(this);
    }

    // 点积运算同样只写一次
    T dot(const VectorBase& other) const {
        T result = T{};
        for (std::size_t i = 0; i < N; ++i) {
            result += data()[i] * other.data()[i];
        }
        return result;
    }

    // 其他公共操作,比如operator-=、标量乘法等都可以在这里实现
    Derived& operator*=(const T& scalar) {
        for (std::size_t i = 0; i < N; ++i) {
            data()[i] *= scalar;
        }
        return *static_cast<Derived*>(this);
    }
};

然后让Vector和VectorView继承这个基类,只需要实现自己特有的逻辑(比如构造、数据持有方式):

// 持有数据的Vector类
template <typename T, std::size_t N>
class Vector : public VectorBase<Vector<T, N>, T, N> {
private:
    T m_data[N];
public:
    Vector() = default;
    Vector(const T values[N]) {
        for (std::size_t i = 0; i < N; ++i) {
            m_data[i] = values[i];
        }
    }

    // 实现基类要求的data()接口
    T* data() { return m_data; }
    const T* data() const { return m_data; }

    // Vector特有的拷贝/赋值逻辑
    Vector(const Vector&) = default;
    Vector& operator=(const Vector&) = default;
};

// 不持有数据的VectorView类
template <typename T, std::size_t N>
class VectorView : public VectorBase<VectorView<T, N>, T, N> {
private:
    T* m_data;
public:
    VectorView() = default;
    VectorView(T* data) : m_data(data) {}

    // 实现基类要求的data()接口
    T* data() { return m_data; }
    const T* data() const { return m_data; }

    // 可以添加View特有的校验逻辑,比如空指针检查
    bool is_valid() const { return m_data != nullptr; }
};

第二步:用通用模板实现自由运算符

对于operator+、operator*这类非成员运算符,不用写4种重载(Vector+Vector、Vector+VectorView等),而是写一个通用模板函数,自动兼容所有VectorBase的派生类:

// 通用的vector+vector运算
template <typename Derived1, typename Derived2, typename T, std::size_t N>
auto operator+(const VectorBase<Derived1, T, N>& lhs, const VectorBase<Derived2, T, N>& rhs) {
    // 结果类型优先选持有数据的Vector(避免返回悬空的View)
    using ResultType = std::conditional_t<
        std::is_same_v<Derived1, Vector<T, N>> || std::is_same_v<Derived2, Vector<T, N>>,
        Vector<T, N>,
        Vector<T, N> // 如果两个都是View,也返回Vector,避免内存风险
    >;

    ResultType result;
    for (std::size_t i = 0; i < N; ++i) {
        result.data()[i] = lhs.data()[i] + rhs.data()[i];
    }
    return result;
}

// 通用的标量乘法
template <typename Derived, typename T, std::size_t N>
auto operator*(const VectorBase<Derived, T, N>& vec, const T& scalar) {
    Derived result = static_cast<const Derived&>(vec);
    result *= scalar;
    return result;
}

template <typename Derived, typename T, std::size_t N>
auto operator*(const T& scalar, const VectorBase<Derived, T, N>& vec) {
    return vec * scalar;
}

如果是C++20及以上,还可以用**概念(Concepts)**约束模板参数,确保传入的类型符合Vector的要求,让代码更安全:

template <typename T>
concept VectorLike = requires(T v) {
    typename T::value_type;
    { v.data() } -> std::same_as<typename T::value_type*>;
    { v.data() const } -> std::same_as<const typename T::value_type*>;
};

// 用Concept约束后的operator+
template <VectorLike Vec1, VectorLike Vec2>
requires (std::same_as<typename Vec1::value_type, typename Vec2::value_type> && Vec1::size == Vec2::size)
auto operator+(const Vec1& lhs, const Vec2& rhs) {
    // 实现逻辑同上
}

最终效果

现在你原来的使用场景完全能正常工作,而且不用重复写任何冗余代码:

Vector<float, 3> ratio {10,10,0};
std::vector<float> image(32*32*3);
VectorView<float, 3> pixel(&image[10*3]);

pixel += ratio; // 直接调用基类的operator+=,修改image的内存
auto sum = ratio + pixel; // 返回Vector<float,3>,自动处理类型
auto scaled_pixel = pixel * 2.0f; // 返回VectorView<float,3>,直接修改原数据

这种设计的好处:

  • 公共逻辑只写一次,后续扩展新操作(比如叉积、矩阵变换)时,只需要在基类或通用模板里添加
  • Vector和VectorView自动获得所有公共操作,不用重复实现
  • 运算符自动兼容两种类型的任意组合,不用手动写多个重载

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:23:00