Consider the following simple classes, which I've contrived based on issues I'm seeing with a real project. Triple is a quick boiler-plate type for use with the inner constexprs in class Foo:
#include <iostream>
class Triple {
public:
friend
std::ostream & operator <<(std::ostream & o, Triple const & t);
constexpr Triple() : a_(0), b_(0), c_(0) { }
constexpr Triple(Triple const & other) = default;
constexpr Triple(double a, double b, double c)
: a_(a), b_(b), c_(c)
{ }
~Triple() = default;
private:
double a_, b_, c_;
};
std::ostream & operator <<(std::ostream & o, Triple const & t) {
o << "(" << t.a_ << ", " << t.b_ << ", " << t.c_ << ")";
return o;
}
class Foo {
public:
Foo() : triple_(defaultTriple) { }
Triple const & triple() const { return triple_; }
Triple & triple() { return triple_; }
constexpr static float defaultPOD{10};
constexpr static Triple defaultTriple{11.0, 22.0, 33.0};
private:
Triple triple_;
};
If I then write a main() function to use the public inner constexprs from Foo, as follows, it will fail to link (using g++ 4.7.0, by way of mingw-x86-64 on Windows 7):
int main(int argc, char ** argv) {
using std::cout;
using std::endl;
cout << Foo::defaultPOD << endl;
cout << Foo::defaultTriple << endl;
}
$ g++ -o test -O3 --std=c++11 test.cpp
e:\temp\ccwJqI4p.o:test.cpp:(.text.startup+0x28): undefined reference to `Foo::defaultTriple' collect2.exe: error: ld returned 1 exit status
However, if I write
cout << Triple{Foo::defaultTriple} << endl
instead of simply
cout << Foo::defaultTriple << endl
it will link and run fine. I can see that the former expresses more explicitly that a compile-time literal is what's intended, but I'm still surprised the latter won't work as well. Is this a compiler bug, or is there a reason based on the rules for constexpr that only the first example should work?
I would try other compilers to get more insight, but at present GCC 4.7.0 is the only one I have access to that supports constexpr.
Note also that the expression for the pod constexpr works fine without an explicit literal wrapper, e.g. cout << Foo::defaultPOD has never given me trouble.
constexprdeclarations insideFoo) outside ofFoo, like so:constexpr Triple Foo::defaultTriple;