表达式模板EvaluatedType trait实现与编译问题解决
解决C++11下表达式模板的EvaluatedType trait实现问题
我刚好碰到过类似的场景,针对你在GCC4.8.5(C++11兼容)下的需求,先梳理下问题根源,再给出可扩展的解决方案:
核心场景与遇到的编译问题
你需要处理多种表达式模板(含第三方库的),要通过EvaluatedType trait获取其求值类型,但因为无法特化模板参数,转而依赖可重载的evaluate函数实现,却遇到两个关键问题:
- 问题1:
EvaluatedType<double>::Type被推导为void,导致(void)a + 5.0这类代码编译失败——本质是运算符优先级陷阱:(void)的优先级高于+,代码实际是将a转为void后尝试和数值相加,这显然不合法,而根源是最初的evaluate1(double)定义要么返回void,要么查找失败导致decltype推导为void。 - 问题2:如果
evaluate1(double)定义在EvaluatedType之后,模板实例化时无法找到该函数——这是C++模板名字查找规则导致的:当实例化EvaluatedType时,只会查找模板定义点之前可见的名字,以及通过ADL(参数依赖查找)找到的命名空间中的名字。
修正后的解决方案
1. 调整函数定义顺序与查找规则
- 算术类型的
evaluate重载必须在EvaluatedType模板定义之前声明/定义,确保模板实例化时能找到; - 自定义类型(比如第三方库的表达式模板)的
evaluate重载要放在与该类型相同的命名空间中,利用ADL自动匹配,无需提前全局声明,方便其他开发者扩展。
2. 修正后的代码实现
首先是基础的trait和通用evaluate函数:
#include <type_traits> // 先定义算术类型的evaluate重载,确保EvaluatedType能找到 template <typename X> typename std::enable_if<std::is_arithmetic<X>::value, double>::type evaluate(const X& x) { return static_cast<double>(x); } // 定义EvaluatedType trait,通过evaluate的返回值推导类型 template <typename X> struct EvaluatedType { using Type = decltype(evaluate(std::declval<X>())); };
然后是自动微分库的适配(放在对应命名空间,利用ADL):
namespace autodif { // 假设这是库中的ScalarBase基类 template <typename X> class ScalarBase { // 库中的实现细节 }; // 该命名空间下的evaluate重载,ADL会自动匹配 template <typename X> xdouble evaluate(const ScalarBase<X>& x) { return x; } }
3. 关键说明
- 对于
double这类算术类型,EvaluatedType<double>::Type会正确推导为double,彻底避免了void类型的错误; - 其他开发者要扩展新类型的求值类型时,只需要在该类型的命名空间中添加对应的
evaluate重载即可,无需修改EvaluatedType本身,完全符合扩展需求; - 代码完全兼容GCC4.8.5的C++11特性,
std::enable_if、std::is_arithmetic、std::declval都是该版本支持的特性。
内容的提问来源于stack exchange,提问作者Bernard GODARD
相关产品推荐
相关产品推荐

