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

Intel C++ 2024编译器编译CRTP模板枚举代码时出现"invalid arithmetic between different enumeration types"错误求助

Intel C++ 2024编译器编译CRTP模板枚举代码时出现"invalid arithmetic between different enumeration types"错误求助

各位大佬,我最近在用Intel C++ 2024编译器编译一段基于CRTP(奇异递归模板模式)的自动微分代码时,碰到了一个棘手的错误:invalid arithmetic between different enumeration types。错误指向的是ExprTimes类中定义枚举的这行代码:enum { numNumbers = LHS::numNumbers + RHS::numNumbers };。

这段代码在GCC和Clang下编译完全正常,但Intel编译器就是卡在这里报错,有没有大佬能帮忙分析下原因,给个解决办法?

我的代码如下:

AAD3.h 头文件代码:

#include <memory>
#include <string>

template <class E> class Expression {};

/************************************************************************************************************************************************************/
// CRTP (Curiously Recurring Template Pattern)
template <class LHS, class RHS>
class ExprTimes : public Expression<ExprTimes<LHS, RHS>> {
    LHS lhs;
    RHS rhs;
public:
    // Constructor
    explicit ExprTimes (const Expression<LHS>& l, const Expression<RHS>& r)
        : lhs(static_cast<const LHS&>(l)), rhs(static_cast<const RHS&>(r)) {}

    double value() const {
        return lhs.value() * rhs.value();
    }

    enum { numNumbers = LHS::numNumbers + RHS::numNumbers }; // 触发错误的行

    std::string writeProgram(
        // On input, the number of nodes processed so far
        // On return the total number of nodes processed on exit
        size_t& processed) {
        // Process the left sub-DAG
        const std::string ls = lhs.writeProgram(processed);
        const size_t ln = - 1;
        // Process the right sub-DAG
        const std::string rs = rhs.writeProgram(processed);
        const size_t rn = processed - 1;
        // Process this node
        const std::string thisString = ls + rs + "y" + std::to_string(processed) + " = y" + std::to_string(ln) + " * y" + std::to_string(rn) + "\n";
        ++processed;
        return thisString;
    }

    // Input: accumulated adjoint for this node or 1 if top node
    void pushAdjoint(const double adjoint) {
        lhs.pushAdjoint(adjoint * rhs.value());
        rhs.pushAdjoint(adjoint * lhs.value());
    }
};

// Operator overload for expressions
template <class LHS, class RHS>
inline ExprTimes<LHS, RHS> operator*(
    const Expression<LHS>& lhs, const Expression<RHS>& rhs) {
    return ExprTimes<LHS, RHS>(lhs, rhs);
}
/************************************************************************************************************************************************************/

template <class ARG>
class ExprLog : public Expression<ExprLog<ARG>> {
    ARG arg;
public:
    // Constructor
    explicit ExprLog(const Expression<ARG>& a)
        : arg(static_cast<const ARG&>(a)) {}

    double value() const {
        return log(arg.value());
    }

    enum { numNumbers = ARG::numNumbers };

    std::string writeProgram(
        // On input, the number of nodes processed so far
        // On return the total number of nodes processed on exit
        size_t& processed) {
        // Process the arg sub-DAG
        const std::string s = arg.writeProgram(processed);
        const size_t n = processed - 1;
        // Process this node
        const std::string thisString = s + "y" + std::to_string(processed) + " = log(y" + std::to_string(n) + ")\n";
        ++processed;
        return thisString;
    }

    // Input: accumulated adjoint for this node or 1 if top node
    void pushAdjoint(const double adjoint) {
        arg.pushAdjoint(adjoint / arg.value());
    }
};

// Operator overload for expressions
template <class ARG>
inline ExprLog<ARG> log(const Expression<ARG>& arg) {
    return ExprLog<ARG>(arg);
}
/************************************************************************************************************************************************************/

// Number type, also an expression
class Number : public Expression<Number> {
    double val;
    std::shared_ptr<double> adj; // Use a shared pointer so that copies of Number hold the same adjoint value (required to make lines 199 and 200 work)
public:
    // Constructor
    explicit Number(const double v)
        : val(v), adj(std::make_shared<double>(0.0)) {}

    // code modified from text to work with shared pointer
    double value() const {
        return val;
    }

    double adjoint() const {
        return *adj; // code modified from text to work with shared pointer
    }

    enum { numNumbers = 1 };

    std::string writeProgram(
        // On input, the number of nodes processed so far
        // On return the total number of nodes processed on exit
        size_t& processed) {
        const std::string thisString = "y" + std::to_string(processed) + " = " + std::to_string(val) + "\n";
        ++processed;
        return thisString;
    }

    void pushAdjoint(const double adjoint) {
        *adj = adjoint; // code modified from text to work with shared pointer
    }
};
/************************************************************************************************************************************************************/

auto calculate(Number t1, Number t2) {
    return t1 * log(t2); // 触发错误的调用行
}
/************************************************************************************************************************************************************/

template <class E>
constexpr auto countNumbersIn(const Expression<E>&) {
    return E::numNumbers;
}

main.cpp 源文件代码:

#include "AAD3.h"

int main() {
    Number x1(2.0), x2(3.0);
    auto e = calculate(x1, x2);
    double e_val = e.
}

问题分析:

Intel C编译器对枚举类型的类型检查比GCC/Clang更严格。这里LHS::numNumbers和RHS::numNumbers分别属于不同类的匿名枚举类型,虽然它们的底层值都是整数,但C标准实际上不允许不同枚举类型的对象直接进行算术运算——GCC和Clang可能做了隐式转换的宽松处理,但Intel编译器严格遵循了这一点,所以抛出了错误。

解决方案:

方案一:显式转换枚举值为整数类型

修改ExprTimes和ExprLog中的枚举定义,把枚举值转换成整数后再相加:

// 在ExprTimes中
enum { numNumbers = static_cast<int>(LHS::numNumbers) + static_cast<int>(RHS::numNumbers) };

// 在ExprLog中
enum { numNumbers = static_cast<int>(ARG::numNumbers) };

如果想更通用,也可以用std::underlying_type_t来获取枚举的底层类型(需要包含<type_traits>头文件):

#include <type_traits>

// ExprTimes中
enum { numNumbers = static_cast<std::underlying_type_t<decltype(LHS::numNumbers)>>(LHS::numNumbers) + static_cast<std::underlying_type_t<decltype(RHS::numNumbers)>>(RHS::numNumbers) };

// ExprLog中
enum { numNumbers = static_cast<std::underlying_type_t<decltype(ARG::numNumbers)>>(ARG::numNumbers) };

方案二:替换枚举为static constexpr int

把所有类中的匿名枚举numNumbers替换成静态常量整数,这样类型完全统一,不会有跨枚举类型运算的问题:

// ExprTimes类中
static constexpr int numNumbers = LHS::numNumbers + RHS::numNumbers;

// ExprLog类中
static constexpr int numNumbers = ARG::numNumbers;

// Number类中
static constexpr int numNumbers = 1;

同时对应的countNumbersIn函数也要调整返回类型:

template <class E>
constexpr int countNumbersIn(const Expression<E>&) {
    return E::numNumbers;
}

总结:

方案二更推荐,因为static constexpr int是C++11及以后标准中更简洁、类型更明确的写法,能更好地兼容各个编译器,也避免了枚举类型带来的潜在类型问题。方案一则适合不想改动太多原有代码的场景,通过显式转换满足Intel编译器的严格检查。

内容来源于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:29:52