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?

SergeyA
  • 61,605
  • 5
  • 78
  • 137
Jeka
  • 1,364
  • 17
  • 33
  • Works here: http://ideone.com/xzky2g – drescherjm Aug 16 '17 at 17:12
  • @drescherjm I tried on ideone as well, and I [could reproduce](http://ideone.com/ADWQrp) the problem (I used C++14 to compile it, though..). – Algirdas Preidžius Aug 16 '17 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 Aug 16 '17 at 17:13
  • I try this code with some different compilers here https://wandbox.org/permlink/ULLcTpSdd4IbUnNH and http://rextester.com/VCPDT93931 – Jeka Aug 16 '17 at 17:14
  • Hmm. Not sure what compiler I choose in ideone. Edit: looks like whatever they are using for c++14. – drescherjm Aug 16 '17 at 17:15

2 Answers2

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.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Could you explaim more what is ODR? And what could I do if I need this code as part of header-only library? – Jeka Aug 16 '17 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 https://connect.microsoft.com/VisualStudio/feedback/details/933699/lnk2005-when-defining-static-const-member – Sneftel Aug 16 '17 at 17:17
  • @Jeka, ODR stands for One Definition Rule. Here is a reference: http://en.cppreference.com/w/cpp/language/definition – SergeyA Aug 16 '17 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 ]

bolov
  • 72,283
  • 15
  • 145
  • 224