Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Criteria for a variable to be usable in a constant expression

This code compiles just fine on all big 4 compilers, even on -pedantic

struct S
{
  constexpr S(int a) {}
};

constexpr int f(S a)
{
  return 1;
}

int main()
{
  int a = 0;
  S s(a);
  constexpr int b = f(s);
}

However, this shouldn't be so according to the standard... right? Firstly, s wouldn't be usable in constant expressions [expr.const]/3, because it fails to meet the criteria of being either constexpr, or, const and of enum or integral type.

Secondly, it is not constant-initialized [expr.const]/2 because the full expression of the initialization would not be a constant expression [expr.const]/10 due to a lvalue-to-rvalue conversion being performed on a variable (a) that is not usable in constant expressions when initializing the parameter of the constructor.

Are all these compilers just eliding the initialization of the parameter of the constructor because it has no side-effects, and is it standard conforming (I'm 99% sure it isn't, as the only way for it to so would be to make s constexpr, and to pass it either a const int or a int that is declared constexpr)?

like image 723
Krystian S Avatar asked Aug 07 '19 07:08

Krystian S


People also ask

What is constant expression?

A constant expression is an expression that can be evaluated at compile time. Constants of integral or enumerated type are required in several different situations, such as array bounds, enumerator values, and case labels. Null pointer constants are a special case of integral constants.

What is constant expression in Python?

A constant expression is an expression that contains only constants. A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur.

What is the meaning of constant expression required error in C?

A constant must be initialized. This error has the following causes and solutions: You tried to initialize a constant with a variable, an instance of a user-defined type, an object, or the return value of a function call.


1 Answers

I believe the magician's trick here is the copy c'tor of S. You omitted it, so a defaulted one is generated for you here. Now it's a constexpr function too.

[class.copy.ctor] (emphasis mine)

12 A copy/move constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration. [ Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use ([basic.def.odr], [class.temporary]). — end note ] If the implicitly-defined constructor would satisfy the requirements of a constexpr constructor ([dcl.constexpr]), the implicitly-defined constructor is constexpr.

Does the evaluation of the copy c'tor run afoul of any of the points in [expr.const]/4? It does not. It doesn't perform an lvalue to rvalue conversion on any of the argument's members (there are none to perform the conversion on). It doesn't use its reference parameter in any way that will require said reference to be usable in a constant expression. So we indeed get a valid constant expression, albeit a non-intuitive one.

We can verify the above by just adding a member to S.

struct S
{
  int a = 1;  
  constexpr S(int a) {}
};  

Now the copy c'tor is trying to access an object that is not usable in a constant expression as part of its evaluation (via said reference). So indeed, compilers will complain.

like image 130
StoryTeller - Unslander Monica Avatar answered Oct 05 '22 02:10

StoryTeller - Unslander Monica