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 expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.19) that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (4.1) is applied toe
, ore
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
compile with
-std=c++17
(or later) where allstatic constexpr
data members are implicitlyinline
and don't need out-of-line definitionsAdd 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 A
s 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
Understanding Gsl::Narrow Implementation
Removing Duplicate Characters from String Using Stl
C++ Passing an Array Pointer as a Function Argument
Vc2013: Function from Bind Not Compiling
Receiving Only Necessary Data with C++ Socket
How to Do Performance Test Using the Boost Library for a Custom Library
Do I Have to Include All These Qt Dlls with My Application
Difference Between Opening a File in Binary VS Text
C++ Access Violation Reading Location 0Xcdcdcdcd Error on Calling a Function
How to Store Different Data Types in One List? (C++)
Correct Way to Define C++ Namespace Methods in .Cpp File
C++11 Change 'Auto' Lambda to a Different Lambda
Confused by Squaring MACro Sqr in C
C++: Unresolved External Symbol _Sprintf and _Sscanf in Visual Studio 2015
What Is the Meaning of This Star (*) Symbol in C++? - Pointer to Member