Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

At what point is a base class member considered to be hidden?

Clang rejects the following program:

struct g { static constexpr bool b = true; };
struct h : g { static constexpr bool b = b; }; // clang nope, gcc ok, msvc ok
static_assert(h::b);

My take on this is that Clang tries to initialize variable b by itself. But, since variable b is still uninitialized, reading from it leads to undefined behavior, which is not allowed in a constant expression.

However, GCC and MSVC both accept this program. Suggesting that they simply resolve h::b's initialization value from its base member g::b. Which begs the questions: At what point does h::b starts to hide its base member g::b?

Of course, qualifying b could solve this discrepancy altogether, but that is besides the point of this question.

Demo


Clang's error message:

<source>:2:38: error: constexpr variable 'b' must be initialized by a
constant expression
    2 | struct h : g { static constexpr bool b = b; };
      |                                      ^   ~
<source>:2:42: note: read of object outside its lifetime is not allowed in a
constant expression
    2 | struct h : g { static constexpr bool b = b; };
      |                                          ^
like image 444
303 Avatar asked Oct 24 '25 21:10

303


1 Answers

Suggesting that they simply resolve h::b's initialization value from its base member g::b. Which begs the questions: At what point does h::b starts to hide its base member g::b?

The locus of the declaration is immediately after the declarator, i.e. here after the b token, before the = token. (see [basic.scope.pdecl]/1)

From that point lookup will be able to find the declared name and so it will hide the base class b. (see [basic.lookup.general]/2 and following)

The second b in static constexpr bool b = b; refers to the member being defined, not to the one in the base class.

The question is whether or not the initialization is well-formed.

First suppose it was not marked constexpr. Then (assuming the proposed resolution of CWG 2821), the initialization would be well-formed and would have well-defined behavior:

b is a static storage duration variable that has zero initialization followed by dynamic initialization. After the zero initialization b's lifetime has begun (under the proposed resolution of CWG 2821). Then reading b during the dynamic initialization would be allowed. b would have value false at this point and initialize b with false again.

Adding constexpr complicates the matter a bit. Now, the initialization must be a constant expression, which would imply that it can't have dynamic initialization, so that the above observation of the zero-initialized state is impossible.

Because the trick with zero-initialization can't work in that case, it can't really work out without undefined behavior. Unfortunately I think that the rules for determining whether the variable's initialization is a constant expression are currently circular for this case and can't give an answer.

Maybe CWG 2186 is relevant: Depending on what "preceding initialization" in [expr.const] means, it may not apply to b when it is used in the initializer. Then the expression can't be a constant expression because the lvalue-to-rvalue conversion on b is not allowed in a constant expression at that point.

like image 200
user17732522 Avatar answered Oct 27 '25 10:10

user17732522



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!