Constexpr Class taking const references not compiling
Yeah, this rule is one of the more complex ones as far as constant evaluation is concerned.
Basically, you cannot have a constexpr reference to an object that doesn't have static storage duration. Taking a reference to an object is basically copying its address - and in order for an object's address to be a constant expression, the address itself needs to be constant - so it has to persist. That is, it needs to be static
.
So if you change the things you're referring to to have static storage duration instead, everything works:
static constexpr int a = 3;
static constexpr int b = 4;
constexpr Operation op(a, b); // now ok
The specific rule your program violates is [expr.const]/10, and T.C. helped me understand how it applies. Declaring a constexpr
variable requires the initialization to be a constant expression ([dcl.constexpr/10]):
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression.
We don't say this, but it makes sense and certainly helps resolve this particular situation, but the "full-expression of the initialization" can be interpreted as a prvalue -- since a prvalue is an expression whose evaluation initializes an object ([basic.lval]/1).
Now, [expr.const]/10 reads:
A constant expression is either a glvalue core constant expression [...], or or a prvalue core constant expression whose value satisfies the following constraints:
- if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
- [...],
- if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
The initialization Operation(a, b)
is a prvalue, so we need each reference data member to refer to an entity that is permitted as a result of a constant expression. Our reference data members refer to a
and b
, neither of which has static storage duration nor are temporaries nor are non-immediate functions. Hence, the overall initialization isn't a constant expression, and is ill-formed.
Making a
and b
static gives them static storage duration, which makes them permitted results of constant expressions, which makes the prvalue initialization satisfy all the requirements, which makes the declaration of op
valid.
This is all a long winded way of saying: when dealing with constant evaluation, everything everywhere has to be constant all the way down. Some of our ways of wording this are very complex (like this one), but it's based on the fundamental idea that the model of constant evaluation is basically like pausing evaluating the code to go run a separate program to produce an answer. Producing op
requires these addresses to be known, fixed things - and that only happens for static storage duration.
Why passing constexpr object by const reference works, but by value doesn't compile
You are missing an initializer on iseq
. You have to add it:
constexpr std::integer_sequence<int, 1,2,3,4> iseq{};
^^
From [dcl.constexpr]:
A
constexpr
specifier used in an object declaration declares the object asconst
. Such an object shall have
literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.20). Otherwise, or if aconstexpr
specifier is used in a reference declaration, every fullexpression
that appears in its initializer shall be a constant expression. [ Note: Each implicit conversion
used in converting the initializer expressions and each constructor call used for the initialization is part of
such a full-expression. —end note ]
[ Example:struct pixel {
int x, y;
};
constexpr pixel ur = { 1294, 1024 }; // OK
constexpr pixel origin; // error: initializer missing
—end example ]
Moreover, as Columbo suggests in his comment and answer, simply being initialized is insufficent. A user-provided constructor is also required, as per [dcl.init]:
If a program calls for the default initialization of an object of a const-qualified type
T
,T
shall be a class type
with a user-provided default constructor.
It's a little odd to have the most relevant section (dcl.constexpr) have an incomplete description of the requirements for a constepxr
object declaration.
Why is a constexpr function on a reference not constexpr?
Unfortunately, the standard states that in a class member access expression The postfix expression before the dot or arrow is evaluated;63 [expr.ref]/1. A postfix expression is a
in a.b
. The note is really interesting because this is precisely the case here:
63) If the class member access expression is evaluated, the subexpression evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.
So data
is evaluated even if it would not be necessary and the rule fore constant expression applies on it too.
Reference fields in constexpr class
Your problem can be reduced to this:
#include <array>
struct X
{
constexpr X() = default;
std::array<int, 2> data{};
int& x { this->data[0]};
};
int main()
{
constexpr auto res = X{};
}
<source>:14:20: error: constexpr variable 'res' must be initialized by a constant expression
constexpr auto res = X{};
^ ~~~
<source>:14:20: note: reference to subobject of 'res' is not a constant expression
<source>:14:20: note: declared here
The issue is that a constexpr reference can be bound only to objects with static storage duration (which this
is not). See how to initialize a constexpr reference
So afaik it is not possible without making it a method (the x()
syntax).
Compiler can't execute constexpr expression
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)
Strange behavior with constexpr interacting with const references
The rule you're violating here:
constexpr X a(x);
Is that a consetxpr
pointer or a constexpr
reference has to refer to an object with static storage duration - that's the only way for its address to itself be a constant expression. That is not the case with x
.
But once you make it so (the const
was redundant):
static constexpr int x = 10;
then the rest works:
constexpr X a(x);
static_assert(f(a).Get() == 10, "This should work.");
The specific rule is [expr.const]/11:
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:
- if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
[...]
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
See also these answers of mine. I should probably just close all of these as dupes of one of these?
constexpr result from non-constexpr call
As mentioned in the comments, the rules for constant expressions do not generally require that every variable mentioned in the expression and whose lifetime began outside the expression evaluation is constexpr
.
There is a (long) list of requirements that when not satisfied prevent an expression from being a constant expression. As long as none of them is violated, the expression is a constant expression.
The requirement that a used variable/object be constexpr
is formally known as the object being usable in constant expressions (although the exact definition contains more detailed requirements and exceptions, see also linked cppreference page).
Looking at the list you can see that this property is required only in certain situations, namely only for variables/objects whose lifetime began outside the expression and if either a virtual function call is performed on it, a lvalue-to-rvalue conversion is performed on it or it is a reference variable named in the expression.
Neither of these cases apply here. There are no virtual functions involved and a
is not a reference variable. Typically the lvalue-to-rvalue conversion causes the requirement to become important. An lvalue-to-rvalue conversions happens whenever you try to use the value stored in the object or one of its subobjects. However A
is an empty class without any state and therefore there is no value to read. When passing a
to the function, the implicit copy constructor is called to construct the parameter of f
, but because the class is empty, it doesn't actually do anything. It doesn't access any state of a
.
Note that, as mentioned above, the rules are stricter if you use references, e.g.
A a;
A& ar = a;
constexpr int kInt = f(ar);
will fail, because ar
names a reference variable which is not usable in constant expressions. This will hopefully be fixed soon to be more consistent. (see https://github.com/cplusplus/papers/issues/973)
Why can I not use a constexpr global variable to initialize a constexpr reference type?
Compile adding const
after int
.
constexpr int const & k = r ;
// ...........^^^^^
The problem is that constepxr
implies const
, so when you define r
constexpr int r =100;
you define constexpr
as an int const
value (also take into account that const
is applied to the type on the left; on the right only if there isn't a type on the left; so const int
and int const
are the same thing).
But your k
constexpr int & k = r ;
isn't a const
(implied by constexpr
) reference to an int const
but only a const
reference to an int
.
And you can't initialize a reference to an int
variable with an int const
value.
You can solve the error by making k
a const
reference to an int const
.
Related Topics
Access to Method Pointer to Protected Method
Using Sendmessage to Send Wm_Close to Another Process
Questions Regarding C++ Non-Pod Unions
Strange Exception Throw - Assign: Operation Not Permitted
What Does "-Wall" in "G++ -Wall Test.Cpp -O Test" Do
Problems with Scanf("%D\N",&I)
Const to Non-Const Conversion in C++
Directx/C++ 3D Engine Programming: Learn Now, or Wait for Directx 12
How to Use Gpu::Stream in Opencv
C++ If Else If Not Working Properly
Set Breakpoint in C or C++ Code Programmatically for Gdb on Linux
How to Set Pointer to a Memory to Null Using Memset
Why Can't I Assign an Array Variable Directly to Another Array Variable with the '=' Operator
C++ Get Handle of Open Sockets of a Program
Right Justifying Output Stream in C++