Следующий пример кода иллюстрирует мою проблему.
constexpr int fact(int N) {
return N ? N * fact(N - 1) : 1;
}
struct A {
int d;
constexpr A operator+(const A& other) const { return A{ fact(d + other.d) }; }
// overload of many other operators
};
int main() {
int x;
cin >> x; // run time argument
constexpr A a{ 2 }, b{ 3 };
A c{ x };
A u = a + b + c; // both + eval at run time
//constexpr A v = a + b + c; // doesn't compile because c is not constant
}
Чего я хочу добиться, так это того, чтобы первый operator+
оценивался во время компиляции, а второй operator+
оценивался во время выполнения.
конечно можно разбить на
constexpr A tmp = a + b;
A u = tmp + c;
но в моем случае весь смысл перегрузки операторов состоит в том, чтобы позволить создавать более сложные формулы более интуитивным способом, что сделало бы перегрузку бессмысленной.
Если я объявлю operator+
как consteval
, то он снова не скомпилируется. И я не могу перегрузить его дважды.
Есть ли решение?
Нет, по крайней мере, в gcc и с оптимизацией здесь вы можете увидеть, как он оценивается во время компиляции. (120
)
main:
mov eax, 1
add edi, 120
je .L4
.L3:
imul eax, edi
sub edi, 1
jne .L3
ret
.L4:
ret
*Справедливости ради, даже без constexpr
компилятор, вероятно, также оптимизировал бы его.
Однако это решение компилятора, которое не гарантируется языком. В сборке выпуска VS2019 мой код дизассемблирования показывает, что оба оцениваются во время выполнения. Я хотел бы знать, можно ли добиться такого же эффекта, как разбить его на constexpr A tmp = a + b; A u = tmp + c;
, не завися от компилятора.
@WhatsUp не работает, даже если вы пишете это таким образом, компилятор не является обязательный для оценки этого во время компиляции (хотя mscv выполняет это во время компиляции)
Да я понимаю, что не принудительно. Но, по крайней мере, я делаю все возможное, чтобы заставить компилятор сделать это...
Вы можете принудительно выполнить оценку с помощью (нетипового) параметра шаблона или функции consteval
.
constexpr int fact(int N) {
return N ? N * fact(N - 1) : 1;
}
struct A {
int d;
constexpr A operator+(const A& other) const { return A{ fact(d + other.d) }; }
};
consteval auto value(auto v){return v;}
A foo (int x) {
constexpr A a{ 2 }, b{ 3 };
A c{ x };
A u = value(a+b) + c;
return u;
}
он оценивается во время компиляции godbolt.org/z/7PYY1cjnv, если вы включаете оптимизацию.