0

Experimentally, I know how to declare/initialize/define static data member of various kinds (mainly by reading the compiler diagnosis) yet I realize that I don't really understand the rules behind these mechanics and the One-Definition-Rule but I have issues with understanding behind the logic of static data member initialization and respect of ODR.
My confusion is increased by differences in behavior according to const-ness, constexpr-ness.
I found several posts on SO speaking about static data members and ODR but I'm unsure that they address exactly my questions below. At least, none that I read was able to give me the big picture, all in one place.

https://en.cppreference.com/w/cpp/language/static provides the rules regarding static data members but I don't fully understand it with respect to One-Defintion-Rule.

Let say I have the following files.

ClassODR.h

struct CData {
#ifdef IN_CLASS_DEFINITION
    static int m_Value = 3;
#else
    static int m_Value;
#endif

#ifdef IN_CLASS_CONST_DEFINITION
    static int const m_kValue = 3;
#else
    static int const m_kValue;
#endif

#ifdef IN_CLASS_CONSTEXPR_DEFINITION
    static double constexpr m_kfValue = 3.14;
#else
    static double constexpr m_kfValue;
#endif
};

#ifdef OUT_OF_CLASS_IN_HEADER_DEFINITION
int CData::m_Value = 3;
#endif

#ifdef OUT_OF_CLASS_CONST_IN_HEADER_DEFINITION
int const CData::m_kValue = 3;
#endif

#ifdef OUT_OF_CLASS_CONSTEXPR_IN_HEADER_DEFINITION
constexpr double m_kfValue = 3.14;
#endif

ClassODR.cpp

#ifndef OUT_OF_CLASS_IN_HEADER_DEFINITION
int CData::m_Value = 3;
#endif

#ifndef IN_CLASS_CONST_DEFINITION
#ifndef OUT_OF_CLASS_CONST_IN_HEADER_DEFINITION
int const CData::m_kValue = 3;
#endif
#endif

#ifndef IN_CLASS_CONSTEXPR_DEFINITION
#ifndef OUT_OF_CLASS_CONSTEXPR_IN_HEADER_DEFINITION
int const CData::m_kfValue = 3.14;
#endif
#endif

OUT_OF_whatever_IN_HEADER_DEFINITION could be only defined if IN_whatever_DEFINITION is not.

TestODRUse#N.cpp

void TestODRUse#N() {
    std::cout << CData::m_Value << '\n';
    std::cout << CData::m_kValue << '\n';
    std::cout << CData::m_kfValue << '\n';
}

main.cpp

int main() {
    TestODRUse1();
    TestODRUse2();  // testing for ODR violations
}

Other trivial header files and #include directives required as glue between the different files are omitted on purpose for clarity sake. The full example is available here.

These are some observations (for the record and the sake of exhaustiveness) and some questions.

1- If any OUT_OF_whatever_IN_HEADER_DEFINITION is set, there is an ODR violation: corresponding variable is defined in TestODR1 and TestODR2 translation unit. No question there. Besides, the situation can be alleviate by declaring the variable inline from C++17 on.

2- I was wondering why the static non-const member could not be initialized in-line, I found that answer that explain that it is in order to avoid ODR violation: https://stackoverflow.com/a/61519186

3- But on the other hand, situation changes as soon as the data is declared const(expr).
static const(expr) member can (must) be initialized in-line: thus each translation unit see a definition but there is no ODR violation: why?

5- Eventually the following paragraph, from the cppreference page linked above, regarding constexpr static data member, puzzles me:

If a const non-inline(since C++17) static data member or a constexpr static data member(since C++11)(until C++17) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. namespace scope for constexpr The following example is given:

struct X
{
    static const int n = 1;
    static constexpr int m = 4;
};
 
const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used
const int X::n;             // … so a definition is necessary
constexpr int X::m;         // … (except for X::m in C++17)

Yet I think that I never had to redefined, out-of-class an in-class initialized static constexpr data member.

To sum it up, I'd like clarification on the ODR for static data members according to their const(expr)-ness. Thanks to specify what may be the differences regarding the different C++ versions.

2
  • I am absolutely sure I saw this question yesterday and commented on it. (10k+ to view deleted Q): stackoverflow.com/questions/77476976/… In general you should improve your existing question rather than deleting and reposting.
    – Ben Voigt
    Commented Nov 14, 2023 at 18:31
  • @BenVoigt Indeed, I merely follow your advice and split the question in two (though I could have cut the original one in two and make a new one, my first impulse was to write 2 questions and delete the original one)
    – Oersted
    Commented Nov 14, 2023 at 19:46

0