I have code something like this:
template<typename ... Args>
constexpr size_t get_init_size(Args ... args) {
return sizeof...(Args);
}
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
I know it is dummy code, but I isolate it to find error.
Compiler give me error(GCC):
In instantiation of 'constexpr auto make_generic_header(Args&& ...) [with Args = {int, int, int}]':
/tmp/tmp.CaO5YHcqd8/network.h:39:43: required from here
/tmp/tmp.CaO5YHcqd8/network.h:31:22: error: 'args#0' is not a constant expression
31 | constexpr size_t header_lenght = get_init_size(args...);
| ^~~~~~~~~~~~~
I tried add qualifier const to function parameters but it same doesn't work. In theory all this function can calculate in compile time. But where is problem I can't find with my knowledge.
CodePudding user response:
It's not about the reference problem. Quoted from the answer https://stackoverflow.com/a/31720324:
The reference does not have a preceding initialization from the point of view of i, though: It's a parameter. It's initialized once ByReference is called.
This is fine, since f does have preceding initialization. The initializer of f is a constant expression as well, since the implicitly declared default constructor is constexpr in this case (§12.1/5). Hence i is initialized by a constant expression and the invocation is a constant expression itself.
And about "preceding initialization", quoted from this:
It does mean "be initialized", but it's more importantly about the visibility of a preceding initialization within the context of the expression being evaluated. In your example, in the context of evaluating func(0) the compiler has the context to see the initialization of rf with 0. However, in the context of evaluating just the expression rf within func, it doesn't see an initialization of rf. The analysis is local, as in it doesn't analyze every call-site. This leads to expression rf itself within func not being a constant expression while func(0) is a constant expression.
Corresponding to your case, the line:
constexpr size_t header_length = get_init_size(args...);
From the point of view of header_length
, get_init_size(args...)
is not a core constant expression, since its invoke argument is from the function's argument, and args is not a core constant expression either.
Simple modification can make your code work, you can remove the constexpr
qualifier from header_length
, or directly return get_init_size(args...);
in make_generic_header
's function body.
I hope this and this might also help you.
CodePudding user response:
constexpr
means different things for variables vs. functions.
For variables, it means that the variable must be compile-time. Therefore, it needs to be initialized with a constant expression.
For functions, constexpr
means the function can be run at compile-time in addition to runtime using the same code inside. Therefore, everything you do inside must be applicable for a runtime call as well.
With that in mind, let's study make_generic_header
:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
Here, header_lenght
is a constexpr
variable, so must be compile-time. Therefore, get_init_size(args...)
must be done at compile-time as well. However, parameters are not constant expressions for various reasons, so this won't work. If this did work, it would mean that make_generic_header
, a constexpr
function, is unusable at runtime¹, which doesn't fit its requirement of being usable at both compile-time and runtime.
The fix is rather simple: Use code that works in both cases:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
size_t header_lenght = get_init_size(args...);
return header_lenght;
}
Did you spot it? The only change is to remove constexpr
from header_lenght
. If this function is run at compile-time, it will still work out and the overall function call expression will be a constant expression.
¹"But it won't work in consteval
either!" - True, the reasoning given in the answer is sufficient for constexpr
, but I've left out the more fundamental reason since it's not relevant here.
CodePudding user response:
I rewrite the code that will be work and I think will be execute in compile time:
template<typename ... Args>
constexpr auto make_generic_header(const Args ... args) {
std::integral_constant<size_t, sizeof...(Args)> header_lenght;
return header_lenght.value;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
I removed function get_init_size
and used code part of template parameter(It is guaranteed that it is will be execute on compile time) and after return the number of arguments passed to function(For std::integral_constant for all object value same and know on compile time)