Why is Foo :: innerConstexpr not referenced, but will UserLiteral {Foo :: innerConstexpr} be?

Consider the following simple classes that I invented based on the problems that I see with a real project. Triple is a quick boiler plate type for use with the internal constexpr in the Foo class:

 #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 the main() function to use the public internal constexpr from Foo , as shown below, it will not be able to reference (using g ++ 4.7.0, via 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 simple

 cout << Foo::defaultTriple << endl 

it will communicate and work fine. I see that the first of them expresses more explicitly what is intended for compilation-time, but I am still surprised that the latter will not work. Is this a compiler error, or is there a rule-based reason for constexpr that only the first example should work?

I would try other compilers to get more information, but currently GCC 4.7.0 is the only one I have access to that supports constexpr .

Note also that the expression for pod constexpr works fine without an explicit literal shell, for example. cout << Foo::defaultPOD never bothered me.

+4
source share
3 answers

A constant expression that appears in a context where a constant expression is not required can be evaluated during program conversion, but it does not have to be, so it could be evaluated at run time.

If the constexpr static member is evaluated during program translation, the compiler can use its initializer to determine its value and does not need to determine the member.

If an element is used in a context that is evaluated at runtime, then its definition will be required.

In cout << Foo::defaultTriple << endl your compiler generates code to perform the lvalue-rvalue Foo::defaultTriple conversion at run time, so the object needs a definition.

In cout << Triple{Foo::defaultTriple} << endl compiler evaluates Foo::defaultTriple during program conversion to create a temporary Triple , which is probably evaluated at runtime.

If your constexpr objects are evaluated only in contexts where constant expressions are required, you must provide them with a definition.

+3
source

defaultPOD and defaultTriple declared inside a class are not a definition. You must define them outside the class declaration if you want to use them in places that should know their address.

So why does cout << Foo::defaultPOD << endl; but cout << Foo::defaultTriple << endl; does not work?

defaultPOD declared as a float , so when you do cout << Foo::defaultPOD , it calls operator<<(float val); which takes its argument by value. This call does not require a definition because you are using only the value (it is not used by odr, as defined in 3.2.3). If you try to pass Foo::defaultPOD function that accepts a link, you need to define it.

However, Foo::defaultTriple fails because operator << accepts Triple by reference requiring the definition of Foo::defaultTriple . However, even after changing operator<< to pass by value in my tests, I still ended up with a linker error. Only if I remove the member variables from Triple and do operator<< pass by value, the code will compile without defining static member variables. (When you delete member variables from Triple , the compiler optimizes from the variable I consider).

(Here is a good link that explains some of these materials).

+2
source

The error occurs from the linker; it cannot find the static member Foo::defaultTriple .

Here the difference arises between a “declaration” and a “definition”. The static string in your class is an declaration, you also need a definition. In C ++, each static field defined inside a class must also be present inside a .cpp file:

 // .hpp class X { static int Q; }; // .cpp int X:Q = 0; 

In your case, you should have this line somewhere in the .cpp file:

 Triple foo::defaultTriple; 
+1
source

All Articles