Strange Behavior with Constexpr Static Member Variable

Strange behavior with constexpr static member variable

The standard does not require any diagnostics for a failure to provide a definition where one is required.

3.2 One definition rule [basic.def.odr]

4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. [...]

This means implementations are allowed to optimise away accesses to such variables, and that's what's happening in your first case with GCC.

Both GCC and clang have decided that they prefer a consistent user experience, where error messages about missing definitions do not depend on the optimisation level. Usually, that means that any missing definition causes an error message. However, in this case, GCC is doing some minimal optimisation even at -O0, avoiding the error.

But the program is an error either way, because even A::dict[0] is an ODR-use:

3.2 One definition rule [basic.def.odr]

3 A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5). [...]

The use of A::dict doesn't involve lvalue-to-rvalue conversion, it involves the array-to-pointer conversion, so the exception doesn't apply.

Why can't a static constexpr member variable be passed to a function?

The problem was neither with the code itself, nor with the standard being used. CLion's default compiler does not fully support C++17, so that's why it showed a strange behavior that it could compile static constexpr member variables, but only as long as they were not passed to functions.

After updating to the most recent compiler version, I was able to run the code successfully without any changes.

Thank you for all your contribution.

Different behavior observed with constexpr auto/char-array variable

A declaration of a static data member in class is never a definition.

The difference between your examples is that only one requires a definition of text.

The auto version deduces char const*, hence text is only subject to an lvalue-to-rvalue conversion and not odr-used. By contrast, the first code effectively passes text's address, odr-use-ing it - i.e. necessitating a definition.

C++ Linker Error With Class static constexpr

I don't think this is a bug. If you change the constexpr to const, it still fails, with the exact same error.

You've declared S::X, but not defined it anywhere, so there's no storage for it. If you do anything with it that needs to know the address of it then you'll need to define it somewhere also.

Examples:

int main() {
int i = S::X; // fine
foo<S::X>(); // fine
const int *p = &S::X; // needs definition
return std::min(S::X, 0); // needs it also
}

The reason for this is that constexpr can be evaluated at compile time, but it's not required to be evaluated as such, and can equally happen at runtime. It doesn't instruct "the compiler to always treat the symbol as an expression", it hints that it would be sensible and permissible to do so if the compiler felt like it.

Undefined reference error to static constexpr data member

Somewhere in your code you are ODR-using numbers but you don't have a definition for it.

Here's a simple version of your problem (wandbox):

#include <iostream>
#include <cstdint>

class shift7seg {
public:
static constexpr std::uint8_t numbers[10] = {};
};

int main() {
// taking the address is ODR-use
std::cout << &shift7seg::numbers[0] << '\n';
}

Possible solutions are

  1. compile with -std=c++17 (or later) where all static constexpr data members are implicitly inline and don't need out-of-line definitions

  2. Add an out-of-line definition in your implementation file (shift7seg.cpp) like this (wandbox):

constexpr std::uint8_t shift7seg::numbers[10];

Linker error for constexpr static member variable in gcc and clang

Before C++17

All the initializations of As invoke the A::A(B ) constructor, which require copying a B, which uses the compiler-generated B::B(B const&). Binding a variable to reference (A::bbb) is an odr-use of that variable. The specific rule, from [basic.def.odr] is:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the
lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any non-trivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e, where
either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

The first call to the copy constructor would involve binding A::bbb to a reference, which is neither an lvalue-to-rvalue conversion nor a discarded-value expression, so it's odr-used.

The most important rule is:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement (6.4.1); no diagnostic required.

A::bbb is odr-used, but lacks a definition, so we're violating that rule - commonly referred to as an odr violation. But since the compiler is not required to issue a diagnostic in this case ("no diagnostic required", or NDR for short), the program is undefined behavior. These kinds of issues can be frustrating at times for a programmer, but they're arbitrarily difficult for the compiler to diagnose - so it's something that we have to live with.

It is likely that on higher optimization levels, the compilers simply elide the copy, so a call to B::B(B const&) for A::bbb isn't necessary... As to why different initializations are treated differently? Probably as a result of different optimization passes. Ultimately, it doesn't change the fact that this is an odr violation - regardless of whether the code compiles and links.



After C++17

As a result of p0386, static constexpr data members are implicitly inline, which means that now A::bbb does have a definition and now there is no odr-violation. C++17 is cool.

Undefined reference to static constexpr char[][]

The definition needs to go outside the class, while the initializer belongs inside the class.

constexpr char A::dict[][3];


Related Topics



Leave a reply



Submit