Understanding static constexpr member variables

24,758

According to the standard 9.4.2/p3 Static data members [class.static.data] (Emphasis Mine):

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.20). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

As M.M earlier explained in the comments ostream::operator<<(ostream&, const complex<T>&) passes by reference so value is considered odr-used in the program. Thus, as the wording above dictates you have to provide a definition.

Now as you’ve already found out fundamental types are passed by value, that it is why no definition required.

Share:
24,758
Titas Chanda
Author by

Titas Chanda

Senior Research Fellow, Quantum Information and Computation, Harish-Chandra Research Institute, Chhatnag Road, Jhunsi, Allahabad - 211 019, Uttar Pradesh, India.

Updated on July 27, 2022

Comments

  • Titas Chanda
    Titas Chanda almost 2 years

    I have some confusions regarding static constexpr member variables in C++11.

    In first.hpp

    template<typename T>
    struct cond_I
    { static constexpr T value = 0; }; 
    
    
    // specialization 
    template<typename T>
    struct cond_I< std::complex<T> >
    { static constexpr std::complex<T> value = {0,1}; }; 
    

    In main() function

    cout << cond_I<double>::value << endl;            // this works fine
    cout << cond_I< complex<double> >::value << endl; // linker error
    

    However if I add the following line to first.hpp everything works fine.

    template<typename T1> 
    constexpr std::complex<T1> cond_I< std::complex<T1> >::value;
    

    What I understand (I may be wrong) is, that cond_I< std::complex<double> >::value needs a definition, but in the previous case it only has the declaration. But then what about cond_I<double>::value? Why it does not require a definition?

    Again, in another header file, second.hpp, I have:

    In second.hpp

    // empty struct
    template<typename T>
    struct eps
    { };
    
    
    // special cases
    template<>
    struct eps<double>
    {
      static constexpr double value = 1.0e-12;
    };
    
    template<>
    struct eps<float>
    {
      static constexpr float value = 1.0e-6;
    };
    

    In this case, following codes works perfectly without any definition of eps<>::value.

    In main() function

    cout << eps<double>::value << endl;    //  works fine
    cout << eps<float>::value << endl;     //  works fine
    

    Can someone please explain me the different behaviors of static constexpr member variables, in these scenarios?

    These behaviors are also the same for gcc-5.2 and clang-3.6.