`constexpr` variable “used in its own initializer”

2019-03-27 03:23发布

问题:

This question seems related to an existing one, but I do not understand the "portable workaround" provided in the answer there (involving const auto this_ = this;) and moreover I think the following example is easier to follow.

I am playing with the following snippet of C++17 code (live demo):

#include <iostream>

struct Test {
  const char* name_{nullptr};
  const Test* src_{nullptr};

  constexpr Test(const char* name) noexcept
    : name_{name}
  {}

  constexpr Test(const Test& src) noexcept
    : src_{&src}
  {
    name_ = src_->name_;
    src_ = nullptr;
  }
};

template<char c>
void print_constexpr_char() {
    std::cout << c << std::endl;
}

int main() {
  constexpr const char* in = "x";
  constexpr auto foo = Test{in};
  constexpr auto bar = Test{foo};
  std::cout << bar.name_ << std::endl;

  print_constexpr_char<bar.name_[0]>();

  return 0;
}

Compilation fails with GCC 7.2 while Clang 5.0.0 does not see any problem. The GCC error essentially reads

error: the value of 'bar' is not usable in a constant expression

note: 'bar' used in its own initializer

I am even more confused after realizing that removing the final print_constexpr_char makes the code compile although it still contains the line constexpr auto bar = Test{foo}; which GCC used to complain about ("used in its own initializer").

  1. Which compiler is correct here?
  2. How to understand the GCC note (if not a bug) that "using in its own initializer" is harmful iff the result is subsequently used in a constant expression?
  3. Is there a valid way/workaround to use pointers in a constexpr constructor as an intermediate stage before transforming the object under construction into the final state which can be stored in a constexpr variable?

回答1:

GCC is correct to reject the code (however the error message could use some work). You cannot use the address of a variable in a constant expression unless that variable has static storage duration.

foo is not static. If you move it outside of main, things will work. Demo

The line marked below is the problem:

constexpr Test(const Test& src) noexcept
: src_{&src} <--- That

Standard reference: (Emphasis mine)

[expr.const]

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:

(5.2) — if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (8.7), the address of a function, or a null pointer value,