I have the following code snippet :
#include <iostream>
// Another function that takes a const char array reference
template <size_t N>
constexpr
int anotherFunction(const char (&str)[N]) {
int ct = 0;
for(int i=0; i< N; i++)
{
ct++;
}
return ct;
}
// emulate the functionality of another wrapper
#define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0);
// Function template that takes a string literal and passes it to another function
template <size_t N, typename... Args>
void printString(const int& log, const char (&str)[N], Args&&... args) {
WRAP(0, str, std::forward<Args>(args)...);
}
int main() {
printString(0, "Hello, World! %s %d", 1, "hello");
return 0;
}
This compiles fine until gcc 9, but since gcc 10 gives the following error:
<source>:25:53: required from here
<source>:15:48: error: 'str' is not a constant expression
15 | #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0);
| ^~~
<source>:19:5: note: in expansion of macro 'WRAP'
19 | WRAP(0, str, std::forward<Args>(args)...);
| ^~~~
Compiler returned: 1
The corresponding godbolt link is : https://godbolt.org/z/zn1a45qMY
I am trying to understand what changes were introduced by reading the changelog, but couldnot make sense of it yet. The above essentially mimics a thirdparty macro that I have been trying to encapsulate into a method in my project. What is going wrong here?
EDIT : I understand that ans need not be a constexpr here. However the macro is actually trimmed down from the thirdparty interface where it is later set as the size of std::array. Hence it needs to be a constexpr(afaiu?). Hence it is not desirable for me to change WRAP macro, however I am at freedom to make changes to printString() to allow the string literal to be passed to the macro. Can I do that somehow?
EDIT 2 : Also it would be interesting to note that this example compiles and runs successfully in gcc14!
Before P2280, naming a reference that doesn't have its lifetime started during the constant expression evaluation and is not usable in constant expressions, even if there is never any access through the reference, always causes the expression to not be a core constant expression.
The parameter of a function can never be usable in constant expressions.
Therefore naming str
in the initialization of ans
, which you require to be a constant expression with constexpr
, makes the initialization not a constant expression and ill-formed.
The old GCC 9 behavior before P2280 was a bug in that it was not standard-conforming.
P2280 was however accepted as a defect report, meaning that it also applies to previous C++ editions. A newer compiler version that implements the defect report should therefore accept the program again. GCC implements P2280 starting with version 14.
However, this all applies only if anotherFunction
doesn't actually use str
other than passing it on by-reference.
However, assuming you actually access str
in anotherFunction
:
The value of a function parameter is never usable in a constant expression. You are trying to force the initialization of ans
to be a constant expression depending on str
, but str
is a function parameter that doesn't have a fixed compile-time known value. Its value depends on the call site of the function. So ans
would also change at each call site making its value not a fixed constant expression as constexpr
would require.
But your code doesn't need constexpr
there anyway. Just remove it. You are not trying to use ans
in a constant expression at compile-time.