如何让C++模板表达式中的operator+正确工作?
表达式模板中operator+与原生类型兼容的问题解决
问题背景
正在为自定义Int类实现C++表达式模板,目标是让Int实例能在表达式模板树中与原生数值类型(如int)正常执行operator+操作,但目前无法实现正确的类型转换。
复现问题的最小代码
#include <type_traits> template <typename E> class IntExpr { public: //typedef typename E::Type Type; //KO static constexpr bool is_leaf = false; template <typename T> T number() const { return static_cast<E const&>(*this).number(); } template <typename T> IntExpr& operator+=(T const& other) { *this = *this + other; return *this; } }; template <typename E1, typename E2> class IntAdd : public IntExpr<IntAdd<E1, E2> > { const E1 u_; const E2 v_; public: static constexpr bool is_leaf = false; IntAdd(E1 const& u, E2 const& v) : u_(u), v_(v) { } }; template <typename T> class Int : public IntExpr<Int<T> > { public: typedef T Type; static constexpr bool is_leaf = true; template <typename E> Int(IntExpr<E> const& expr) { number_ = expr.number(); } explicit Int(int number) : number_{number} { } T number() const { return number_; } Int& operator+=(Int const& other) { number_ += other.number_; return *this; } private: T number_; }; template <typename E1, typename E2> inline IntAdd<E1, E2> operator+(IntExpr<E1> const& u, IntExpr<E2> const& v) { return IntAdd<E1, E2>(*static_cast<const E1*>(&u), *static_cast<const E2*>(&v)); } template <typename T, typename S, typename std::enable_if_t<!std::is_base_of_v<S, T> >* = nullptr> inline decltype(auto) operator+(IntExpr<T> const& lhs, S const& rhs) { //return lhs + Int<typename IntExpr<T>::Type>(rhs); //KO return lhs + T(rhs); } template <typename T, typename S, typename std::enable_if_t<!std::is_base_of_v<S, T> >* = nullptr> inline decltype(auto) operator+(S const& lhs, IntExpr<T> const& rhs) { //return rhs + Int<typename IntExpr<T>::Type>(lhs); //KO return rhs + T(lhs); } int main() { auto const a((Int<int>(1) + 2) + 3); auto const b((Int<long>(1) + 2) + 3); return 0; }
报错信息
当尝试将Int与原生int相加时,编译器报错:
D:\programmation\cpp\TestTemplateExpression\main.cpp: In instantiation of 'decltype(auto) operator+(const IntExpr<E1>&, const S&) [with T = IntAdd<Int<int>, Int<int> >; S = int; std::enable_if_t<(! is_base_of_v<S, T>)>* <anonymous> = 0]': D:\programmation\cpp\TestTemplateExpression\main.cpp:96:38: required from here D:\programmation\cpp\TestTemplateExpression\main.cpp:84:18: error: no matching function for call to 'IntAdd<Int<int>, Int<int> >::IntAdd(const int&)' 84 | return lhs + T(rhs); | ^~~~~~ D:\programmation\cpp\TestTemplateExpression\main.cpp:34:9: note: candidate: 'IntAdd<E1, E2>::IntAdd(const E1&, const E2&) [with E1 = Int<int>; E2 = Int<int>]' 34 | IntAdd(E1 const& u, E2 const& v) : u_(u), v_(v) | ^~~~~~ D:\programmation\cpp\TestTemplateExpression\main.cpp:34:9: note: candidate expects 2 arguments, 1 provided D:\programmation\cpp\TestTemplateExpression\main.cpp:26:7: note: candidate: 'constexpr IntAdd<Int<int>, Int<int> >::IntAdd(const IntAdd<Int<int>, Int<int> >&)' 26 | class IntAdd : public IntExpr<IntAdd<E1, E2> > | ^~~~~~ D:\programmation\cpp\TestTemplateExpression\main.cpp:26:7: note: no known conversion for argument 1 from 'const int' to 'const IntAdd<Int<int>, Int<int> >&' D:\programmation\cpp\TestTemplateExpression\main.cpp:26:7: note: candidate: 'constexpr IntAdd<Int<int>, Int<int> >::IntAdd(IntAdd<Int<int>, Int<int> >&&)' D:\programmation\cpp\TestTemplateExpression\main.cpp:26:7: note: no known conversion for argument 1 from 'const int' to 'IntAdd<Int<int>, Int<int> >&&'
尝试通过IntExpr获取嵌套类型Type时,因类不完整导致报错:
:\programmation\cpp\TestTemplateExpression\main.cpp: In instantiation of 'class IntExpr<Int<int> >': D:\programmation\cpp\TestTemplateExpression\main.cpp:41:7: required from 'class Int<int>' D:\programmation\cpp\TestTemplateExpression\main.cpp:96:29: required from here D:\programmation\cpp\TestTemplateExpression\main.cpp:7:34: error: invalid use of incomplete type 'class Int<int>' 7 | typedef typename E::Type Type; //KO | ^~~~ D:\programmation\cpp\TestTemplateExpression\main.cpp:41:7: note: declaration of 'class Int<int>' 41 | class Int : public IntExpr<Int<T> > | ^~~
核心需求
需要实现一个类型萃取工具,能够从任意表达式模板节点(无论是叶子节点Int<T>还是复合节点如IntAdd<E1,E2>)中提取出底层的数值类型T,从而将原生数值转换为对应的Int<T>实例参与表达式运算。
解决思路:类型萃取器
通过递归的类型萃取模板,区分叶子节点和复合节点:
- 对于叶子节点
Int<T>,直接返回T - 对于复合节点(如
IntAdd<E1,E2>),递归萃取其左操作数的底层类型(或根据需求选择右操作数,或做类型提升)
完整修复代码
#include <type_traits> #include <iostream> // 类型萃取器:提取表达式的底层数值类型 template<typename E> struct underlying_type; template<typename T> struct underlying_type<Int<T>> { using type = T; }; template<typename E1, typename E2> struct underlying_type<IntAdd<E1, E2>> { // 递归提取左操作数的底层类型,也可根据需求做类型提升(如std::common_type_t) using type = typename underlying_type<E1>::type; }; // 便利别名 template<typename E> using underlying_type_t = typename underlying_type<E>::type; template <typename E> class IntExpr { public: static constexpr bool is_leaf = false; template <typename T> T number() const { return static_cast<E const&>(*this).number(); } template <typename T> IntExpr& operator+=(T const& other) { *this = *this + other; return *this; } }; template <typename E1, typename E2> class IntAdd : public IntExpr<IntAdd<E1, E2> > { const E1 u_; const E2 v_; public: static constexpr bool is_leaf = false; IntAdd(E1 const& u, E2 const& v) : u_(u), v_(v) {} // 实现number()方法,计算表达式结果 auto number() const { return u_.number() + v_.number(); } }; template <typename T> class Int : public IntExpr<Int<T> > { public: typedef T Type; static constexpr bool is_leaf = true; template <typename E> Int(IntExpr<E> const& expr) : number_(expr.number()) {} // 支持从原生数值构造 template<typename S, std::enable_if_t<std::is_arithmetic_v<S>>* = nullptr> explicit Int(S number) : number_(static_cast<T>(number)) {} T number() const { return number_; } Int& operator+=(Int const& other) { number_ += other.number_; return *this; } private: T number_; }; template <typename E1, typename E2> inline IntAdd<E1, E2> operator+(IntExpr<E1> const& u, IntExpr<E2> const& v) { return IntAdd<E1, E2>(*static_cast<const E1*>(&u), *static_cast<const E2*>(&v)); } // 原生数值与表达式相加:转换为对应底层类型的Int实例 template <typename E, typename S, std::enable_if_t<std::is_arithmetic_v<S>>* = nullptr> inline decltype(auto) operator+(IntExpr<E> const& lhs, S const& rhs) { using IntType = Int<underlying_type_t<E>>; return lhs + IntType(rhs); } template <typename E, typename S, std::enable_if_t<std::is_arithmetic_v<S>>* = nullptr> inline decltype(auto) operator+(S const& lhs, IntExpr<E> const& rhs) { using IntType = Int<underlying_type_t<E>>; return IntType(lhs) + rhs; } int main() { auto const a((Int<int>(1) + 2) + 3); auto const b((Int<long>(1) + 2) + 3); std::cout << "a's value: " << a.number() << std::endl; // 输出6 std::cout << "b's value: " << b.number() << std::endl; // 输出6 return 0; }
关键改进点
- 类型萃取器
underlying_type:递归解析表达式节点的底层数值类型,解决了IntExpr中无法直接访问E::Type的问题(避免了类不完整的错误)。 - 优化
Int的构造函数:添加了通用的原生数值构造函数,支持所有算术类型转换。 - 修正
operator+重载:利用萃取器获取的类型创建对应的Int实例,确保原生数值能正确转换为表达式模板兼容的类型。 - 补充
IntAdd的number()方法:实现表达式的计算逻辑,让表达式模板能正确求值。
临时解决方案(固定类型)
如果暂时不需要动态提取类型,可采用固定类型的转换,但会丢失类型信息:
#include <iostream> #include <type_traits> template <typename E> class IntExpr { public: static constexpr bool is_leaf = false; constexpr decltype(auto) number() const { return static_cast<E const&>(*this).number(); } }; template <typename E1, typename E2> class IntAdd : public IntExpr<IntAdd<E1, E2> > { public: constexpr IntAdd(E1 const& u, E2 const& v) : u_(u), v_(v) {} constexpr decltype(auto) number() const { return u_.number() + v_.number(); } private: E1 const u_; E2 const v_; }; template <typename T> class Int : public IntExpr<Int<T> > { public: static constexpr bool is_leaf = true; template <typename E> constexpr Int(IntExpr<E> const& expr) : number_(expr.number()) {} template <typename S, std::enable_if_t<std::is_standard_layout_v<S> && std::is_trivial_v<S> >* = nullptr> constexpr Int(S const& number) : number_(static_cast<T>(number)) {} constexpr T const& number() const { return number_; } private: T number_{0}; }; template <typename E1, typename E2> constexpr inline IntAdd<E1, E2> operator+(IntExpr<E1> const& u, IntExpr<E2> const& v) { return IntAdd<E1, E2>(*static_cast<const E1*>(&u), *static_cast<const E2*>(&v)); } template <typename E, typename T, typename std::enable_if_t<std::is_standard_layout_v<T> && std::is_trivial_v<T> >* = nullptr> constexpr inline decltype(auto) operator+(IntExpr<E> const& u, T const& v) { return IntAdd<E, Int<char> >(*static_cast<const E *>(&u), Int<char>(v)); } template <typename E, typename T, typename std::enable_if_t<std::is_standard_layout_v<T> && std::is_trivial_v<T> >* = nullptr> constexpr inline decltype(auto) operator+(T const& u, IntExpr<E> const& v) { return IntAdd<Int<char>, E>(Int<char>(u), *static_cast<const E *>(&v)); } int main() { auto const a(Int<long>(1)); auto const b(Int<long>(1) + 2); auto const c((Int<long>(1) + 2) + 3); std::cout << "a type " << typeid(a).name() << std::endl; std::cout << "a number " << a.number() << std::endl; //expected 1 std::cout << "b type " << typeid(b).name() << std::endl; std::cout << "b number " << b.number() << std::endl; //expected 3 std::cout << "c type " << typeid(c).name() << std::endl; std::cout << "c number " << c.number() << std::endl; //expected 6 return 0; }
内容的提问来源于stack exchange,提问作者P'tit Ju
相关产品推荐
相关产品推荐

