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; }
关键修改说明
expr类添加size()方法:通过std::apply从第一个操作数获取大小,假设数值库中所有操作数的元素数量一致。tridiagonal新增模板构造函数:接受任意支持operator[]和size()的类型,初始化时先匹配源的容器大小,再逐个赋值元素。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
相关产品推荐
相关产品推荐

