4

I have a weird behaviour with static integal constants:

#include <iostream>
#include <inttypes.h>

class Test{
public:
 static const uint32_t Magic = 0x1123;
};

class DataStream{
public:
    template<typename T>
    DataStream& operator <<( const T& value )
    {
        std::cout << value << std::endl;
        return *this;
    }
};

int main()
{
    DataStream s;

    uint32_t a = Test::Magic;  // ok
    bool compare = ( a == Test::Magic ); // ok
    s << compare;
    s << a;
    s << Test::Magic;  // fail

    return 0;
}

I know that such constants should be defined outside of class in .cpp as

const uint32_t Test::Magic;

But strange things is that code above work fine with out line s << Test::Magic; and produce error only if Magic used with template operator << directly.

Even more error undefined reference to 'Test::Magic' appear with GCC, but not with MSVC.

The question is why I should define Test::Magic outside of class (even without value!!!), and why my code work fine in some conditions even without such definition?

5
  • Works here: ideone.com/xzky2g
    – drescherjm
    Commented Aug 16, 2017 at 17:12
  • @drescherjm I tried on ideone as well, and I could reproduce the problem (I used C++14 to compile it, though..). Commented Aug 16, 2017 at 17:13
  • 1
    References to static constant integral members are often elided by the compiler (which knows the value at compile time, so it just uses it directly). I forget where in the C++ spec this is allowed/expected to happen, though.
    – Cameron
    Commented Aug 16, 2017 at 17:13
  • I try this code with some different compilers here wandbox.org/permlink/ULLcTpSdd4IbUnNH and rextester.com/VCPDT93931
    – Jeka
    Commented Aug 16, 2017 at 17:14
  • Hmm. Not sure what compiler I choose in ideone. Edit: looks like whatever they are using for c++14.
    – drescherjm
    Commented Aug 16, 2017 at 17:15

2 Answers 2

2

As a rule, all static const names should be defined in .cpp file if they are ODR used. Taking a reference to the argument is ODR using them. However, having violated this rule is undefined behavior, and MSVC not reporting an error is just one of the ways undefined behavior can be manifested.

As a practical consideration, you are likely to have the error when function is not inlined, and probably won't see it for the inlined functions. My guess is that inlining works differently with the level of optimization you are using for those compilers.

3
  • Could you explaim more what is ODR? And what could I do if I need this code as part of header-only library?
    – Jeka
    Commented Aug 16, 2017 at 17:17
  • It's actually worse than that.... in older versions of MSVC, defining a static const member variable in one translation unit and using it from another translation unit led to a multiple definition linker error. See connect.microsoft.com/VisualStudio/feedback/details/933699/…
    – Sneftel
    Commented Aug 16, 2017 at 17:17
  • @Jeka, ODR stands for One Definition Rule. Here is a reference: en.cppreference.com/w/cpp/language/definition
    – SergeyA
    Commented Aug 16, 2017 at 17:23
1

§ 9.4.2 Static data members [class.static.data]

  1. 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 [...] 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.

  2. [ Note: There shall be exactly one definition of a static data member that is odr-used (3.2) in a program; no diagnostic is required. —end note ]

Not the answer you're looking for? Browse other questions tagged or ask your own question.