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.