29

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.

4
  • 5
    I think the answer is that because ostream::operator<<(ostream&, const complex<T> &) passes by reference, this is considered odr-use of the argument, therefore you need a definition (for the reference to refer to). Commented Dec 2, 2015 at 21:55
  • 1
    Yes. Without taking any reference, first code works. float or double are passed by value, so the second code works. Thank you. Commented Dec 2, 2015 at 22:07
  • Related: stackoverflow.com/q/32812663/3093378 Commented Dec 3, 2015 at 4:45
  • @vsoftco Thank you for the note. That thread contains some useful explanations. I didn't find it earlier. Commented Dec 3, 2015 at 17:51

1 Answer 1

19

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.

Sign up to request clarification or add additional context in comments.

2 Comments

for anyone else confused at this: from C++17, due to changes that affect inline variables in general, the namespace definition becomes unnecessary (but still allowed / ignored for backwards compat)
Workaround: create a copy of it just before using template<typename T> T copy(T v){ return v; } It will create a temporary, and a reference to constant can be taken from a temporary. Will be optimized easily.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.