-1

This is a frustratingly difficult question to ask. I'm asking you to guess what code or conditions in my build system might allow the following to build and run correctly, when the obvious expectation is that it should result in a linker undefined reference error.

class Logger
{
public:
  template <typename T>
  Logger & operator<<(const T& r)  // odr-use of static const data member here.
  { return *this;
  }
  //...
};
class SomeClass
{
  static const int a_constant = 42; // used as enum, not defined elsewhere.
  static Logger & logOutput()
  {  static Logger log;
     return log;
  }

  void do_something()
  {  logOutput() 
      << a_constant     //<--- EXPECT undefined reference error here.
      << '\n';          // works fine also.
  }
};

(Dev is g++ 9.xx, -std=c++17. CI builds are g++ 4.6.x.)

Code similar to the above builds without error, against expectations, in our development environment. It had also built in the past without issue in our Jenkins CI environment, most recently 3 years ago. The reason to ask is that it now no longer builds in CI, but continues to build fine in dev. That it now fails to build in CI brings it to our attention.

My expectation is template<> Logger::operator<<(const int &) will always be preferred, given that the offending value is a const int. I don't expect that a free function can ever be a better overload match. (Is this a safe presumption?)

What mechanisms could allow this white magic? Compiler flags? versions? Other helper function signatures?

Thanks.

MikeW
  • 89
  • 1
  • 4
  • Please, post a [repro] that shows what goes wrong without adding unnecessary fuss. I see that `print` is not needed, for instance. – Enlico Apr 25 '21 at 05:03
  • The question is actually the opposite: how can it work *without* defining the member var. I've clarified the question above. Thanks. – MikeW Apr 25 '21 at 07:13
  • A practical explanation: the constant can be applied at compile-time so the runtime program does not need to have it allocated in memory as long as the address is not taken. Thus no definition is needed. Think of it as a constant defined using `#define`. – nielsen Apr 25 '21 at 07:41

1 Answers1

1

What version of C++ is your CI using? If it's C++17 (you should tag your question with the appropriate version, too) you can just declare the class member variable inline static like so: inline static const int a_constant{42}; and it will be declared and defined.

Casey
  • 10,297
  • 11
  • 59
  • 88
  • Thanks. I like the inline declaration better than changing it to an enum. – MikeW Apr 25 '21 at 07:11
  • @MikeW If this answered your question, please mark it as Accepted. – Casey Apr 25 '21 at 08:29
  • It does not answer the question. The question remains: what unseen code or environment conditions allows odr-use without a definition in the call to `template <> Logger& Logger::operator<<(const int &)`.. – MikeW Apr 25 '21 at 14:35
  • @MikeW Changing your question after the fact is frowned upon. Ask a new question. – Casey Apr 25 '21 at 16:41
  • It's the same question. Unchanged. Clarified. – MikeW Apr 25 '21 at 18:39