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

CppCon2019表达式模板实现:tridiagonal类赋值编译错误求助

问题原因

编译错误的核心是:a + b返回的是expr类型的临时对象,而tridiagonal c = ...属于拷贝初始化操作,需要tridiagonal提供能接受该expr对象的构造函数。但当前tridiagonal仅实现了模板赋值运算符,赋值运算符无法用于初始化阶段的类型转换。

解决方案

给tridiagonal添加模板构造函数,用于从任意支持operator[]的类型(包括表达式模板expr)创建对象;同时为expr和tridiagonal补充size()方法,确保能正确获取元素数量以初始化容器。

修改后的完整代码

#include <vector>
#include <tuple>
#include <functional>
#include <type_traits>

// is_array_v
template <class T> struct is_array {
    static constexpr bool value = false;
};
template <class T>
struct is_array<std::vector<T>> {
    static constexpr bool value = true;
};
template <class T>
constexpr bool is_array_v =
    is_array<std::remove_cvref_t<T>>::value;

struct expression {};
template <class callable, class... operands>
class expr : public expression
{
    std::tuple<operands const &...> args_;
    callable f_;
public:
    expr(callable f, operands const&... args)
        : args_(args...), f_(f) {}
    
    auto operator[](size_t const i) const
    {
        auto const call_at_index =
            [this, i](operands const&... a)
                {
                return f_(subscript(a, i)...);
            };
        return std::apply(call_at_index, args_);
    }

    // 从第一个操作数获取表达式的元素数量
    size_t size() const {
        return std::apply([](auto const& first, auto const&...) {
            return first.size();
        }, args_);
    }
};

template <class T>
constexpr bool is_array_or_expression =
    is_array_v<T> ||
    std::is_base_of_v<expression, std::remove_cvref_t<T>>;

// subscript()
template <class operand>
auto subscript(operand const& v, size_t const i) {
    if constexpr (is_array_or_expression<operand>) {
        return v[i];
    }
    else {
        return v;
    }
}

template <class LHS, class RHS>
auto operator+(LHS const & lhs, RHS const& rhs)
{
    return expr{
        [](auto const& l, auto const& r)
        {
            return l + r;
        }, lhs, rhs
    };
}

class tridiagonal
{
    std::vector<double> v_;
public:
    tridiagonal(std::vector<double> v) : v_(std::move(v)) {}

    // 新增模板构造函数:从表达式/数组类型初始化tridiagonal
    template <class src_type>
    tridiagonal(src_type const& src)
        : v_(src.size()) { // 先根据源的大小初始化内部容器
        for (size_t i = 0; i < v_.size(); ++i) {
            v_[i] = src[i];
        }
    }

    // 保留原模板赋值运算符
    template <class src_type>
    tridiagonal& operator=(src_type const& src)
    {
        size_t const I = v_.size();
        for (size_t i = 0; i < I; ++i) {
            v_[i] = src[i];
        }
        return *this;
    }

    // 新增size()方法,供expr获取元素数量
    size_t size() const {
        return v_.size();
    }
};

// 客户端测试代码
int main() {
    tridiagonal a({ 1.0, 2.0, 3.0 });
    tridiagonal b({ 0.5, 0.25, -4.5 });
    tridiagonal c = a + b; // 现在可正常编译
    return 0;
}

关键修改说明

  1. expr类添加size()方法:通过std::apply从第一个操作数获取大小,假设数值库中所有操作数的元素数量一致。
  2. tridiagonal新增模板构造函数:接受任意支持operator[]和size()的类型,初始化时先匹配源的容器大小,再逐个赋值元素。
  3. tridiagonal补充size()方法:既符合数值容器的常规接口,也为expr提供获取大小的能力。

额外优化建议

  • 给operator+添加约束,避免干扰内置类型的加法:
    template <class LHS, class RHS>
    requires is_array_or_expression<LHS> || is_array_or_expression<RHS>
    auto operator+(LHS const & lhs, RHS const& rhs)
    {
        // 原有实现
    }
    
  • 可在构造/赋值时添加大小检查,对不匹配的操作数抛出异常或做兼容处理。

内容的提问来源于stack exchange,提问作者11house

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:44:59