Here is a problem the reason of which is quite obscure to me, but the workaround of which is fortunately quite easy.
Consider the following code (let me call it my main.cpp
):
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
return std::min(zero, BAR);
}
};
int main() {
Foo foo;
foo();
}
When I tried to compile it, I got the error:
foobar:~/stackoverflow$ g++ -std=c++11 main.cpp
/tmp/ccjULTPy.o: In function 'Foo::operator()() const':
main.cpp:(.text._ZNK3FooclEv[_ZNK3FooclEv]+0x1a): undefined reference to `Foo::BAR'
collect2: error: ld returned 1 exit status
The same happens (quite obviously) also if I use the following statement:
return std::min(zero, Foo::BAR);
Below a slightly modified version of the example above.
This one compiles with no error, even though I'm still referring to the BAR
member:
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
float bar = BAR;
return std::min(zero, bar);
}
};
int main() {
Foo foo;
foo();
}
I didn't succeed in understanding why the latter version compiles fine while the former ends with an error.
As far as I know, both the versions are correct and should compile, but I strongly suspect that I'm missing something important here.
Any suggestion?
Here my compiler's version: g++ (Debian 5.3.1-5) 5.3.1 20160101
.
The selected prototype for min
is
template<class T>
/* constexpr since C++14 */ const T& min( const T& a, const T& b );
The pertinent point is that it takes the argument by reference, meaning it One-Definition-Rule (ODR)-uses it.
And you never defined it, you only declared it in your class (with an initializer):
static constexpr float BAR = .42;
Which is good enough for copying and otherwise using the value, but not for using it as anything but a prvalue.
See Why does constexpr static member (of type class) require a definition?
Violation of the ODR (whose finer points are fine and voluminuous indeed) need not be diagnosed:
3.2 One definition rule [basic.def.odr]
4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is odr-used.