6

I have learnt about constexpr variables in c++ and read that int i =0; constexpr int j = i; fails because i is not a constant expression which makes sense. But then when I did the same with a variable of a class, it worked.

struct C{};
int main()
{
    int i = 0; //i is not a constant expression as expected 
    //constexpr int j = i; //this fails AS EXPECTED

    C c; 
    constexpr C d = c; //WHY DOESN'T THIS FAIL?? 
}

As you can see, constexpr C d = c; compiles without any problem unlike constexpr int j = i; even though c is also not a constant expression.

I want to know the reason behind this.

Alan
  • 116
  • 9
  • Because the class has an **implicit `constexpr` copy constructor** that can be used here. – Jason Mar 14 '23 at 06:05
  • It is allowed, because none of [the requirements](https://en.cppreference.com/w/cpp/language/constant_expression) for a _core constant expression_ are violated by the initialization of `d`. In particular there is no lvalue-to-rvalue conversion if the class is empty, because no scalar value is read or written. The initialization of `d` does not depend on the _value_ of `c`, only on its type. – user17732522 Mar 14 '23 at 08:32

2 Answers2

1

I want to know the reason behind this.

Because the class C has an implicit constexpr copy constructor constexpr C::C(const C&) that can be used in constexpr context like this.

Basically the initialization constexpr C d = c; is a constexpr context and the constexpr ctors are designed exactly for these purposes.


You can also see this by looking at the generated code at cppinsights:

struct C
{
  // inline constexpr C() noexcept = default;
  // inline constexpr C(const C &) noexcept = default;
};

As we can see in the above generated code, C has implicitly declared constexpr constructors that can be used in constexpr contexts.


From constexpr:

A constexpr variable must satisfy the following requirements:

  • ...
  • the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
Jason
  • 36,170
  • 5
  • 26
  • 60
  • But this does not explain why a `constexpr` variable can be defined with a non-const initializer. – j6t Mar 14 '23 at 06:31
  • @j6t Because the initialization `constexpr C d = c;` is a constexpr context and the constexpr ctors are designed exactly for these purposes. – Jason Mar 14 '23 at 07:00
  • Is then `constexpr int j = i;` not a constexpr context? – j6t Mar 14 '23 at 07:34
  • @j6t That is exactly my point. The problem with `constexpr int j = i;` is that `i` is a built in type and has no `constexpr` copy ctor(or any other constexpr ctor for that matter). – Jason Mar 14 '23 at 07:36
  • 1
    I still don't buy the argument. The explanation cannot be reduced to the constexpr copy-constructor. If we have a member in `C`, `struct C { int k; }`, then the copy-constructor is still `constexpr`, but compilation fails now. – j6t Mar 14 '23 at 07:41
  • @j6t I've added some reference, see if it makes sense now. In particular, cppreference says: *"A constexpr variable must satisfy the following requirements: the **full-expression of its initialization, including** all implicit conversions, **constructors calls, etc, must be a constant expression**"* – Jason Mar 14 '23 at 07:43
  • After your edit, why is the initializer `c` a constant expression? It has been declared neither `const` nor `constexpr`. – j6t Mar 14 '23 at 07:44
  • The initializer `c` is not a constant expression. I am not claiming that `c` is a constant expression. The full-expression of a variable's initialization must be a constant expression which is satisfied in this example because of `C`'s constexpr copy ctor. – Jason Mar 14 '23 at 07:45
  • But `d` needs to be evaluated and initialized at compile time, right? How is it possible if the copy ctor is called at runtime? – digito_evo Mar 14 '23 at 08:13
0

Its valid because the initialization of d doesn't depend on the value of c since c contains no data that exists while runtime.

struct A{
    static int x;
};

int A::x = 5;

struct B{
    int x = 5;
};

int main() {
    A a1;
    constexpr A a2 = a1; // a1 contains no runtime data: okay

    B b1;
    constexpr B b2 = b1; // b1 contains runtime data: error
}
g++ main.cpp 
main.cpp: In function ‘int main()’:
main.cpp:16:22: error: the value of ‘b1’ is not usable in a constant expression
   16 |     constexpr B b2 = b1; // a1 containes runtime data: error
      |                      ^~
main.cpp:15:7: note: ‘b1’ was not declared ‘constexpr’
   15 |     B b1;
      |       ^~

You can find the list of requirements for a constant expression at:

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51