Compilation of (a solution containing) this code in a AAD3.h
header :
#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 }; // The line yielding ultimately the error
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); // the line triggering the error
}
/************************************************************************************************************************************************************/
template <class E>
constexpr auto countNumbersIn(const Expression<E>&)
{
return E::numNumbers;
}
and the following code in a main.cpp
source :
#include "AAD3.h"
int main()
{
Number x1(2.0), x2(3.0);
auto e = calculate(x1, x2);
double e_val = e.value();
int cnt = countNumbersIn(e);
e.pushAdjoint(1.0);
double x1_adj = x1.adjoint();
double x2_adj = x2.adjoint();
}
yields to no error with platform toolset
equal to Visual Studio 2022 (v143)
, even with C++ Language Standard
equal to Preview - Features from the Latest C++ Working Draft (/std:c++latest)
. But if I set platform toolset
to Intel C++ Compiler 2025
(or Intel C++ Compiler 2024
) with C++ Language Standard
equal to Preview - Features from the Latest C++ Working Draft (/std:c++latest)
the compilation triggers the following error :
Build started at 09:56...
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug x64 ------
1>In file included from C:\CODING\OTHERS\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp:1:
1>C:\CODING\OTHERS\ConsoleApplication1\ConsoleApplication1\AAD3.h(34,38): : error : invalid arithmetic between different enumeration types ('Number::(unnamed enum at ./AAD3.h:151:2)' and 'ExprLog<Number>::(unnamed enum at ./AAD3.h:94:2)')
1> 34 | enum { numNumbers = LHS::numNumbers + RHS::numNumbers };
1> | ~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
It compiles fine with those Intel compilers only when C++ Language Standard
is equal to ISO C++20 Standard (/std:c++20)
or lower.
I tried naively to cast both elements of the last line to int
with no success. I don't see how to correct the code to get rid of this error when I use the intel compiler.
What explains that the use of /std:c++latest
triggers this error ? (Only with Intel compiler and not with the Microsoft's ones.)
(Also, I added the icc
tag in case it is useful even if I think that it doesn't apply to recent or semi-recent intel compile like the Intel C++ 2024/2025 compilers (from Intel One APÏ 2024.2 and 2025.1) that I am using.)
This is C++ 2026's P2864R2 "Remove deprecated arithmetic conversion on enumerations" implemented in Intel C++ 2024 and 2025 compilers but not yet in Microsoft's v143 with std:latest
. You'd have the same with clang's current trunk version wtih `-std=c++26`, while g++ current trunk version would only issue a warning.
To correct your code regarding this, you could replace the numNumbers
enumeration with static const int numNumbers
, for instance.