Reputation: 60461
Consider this code :
#include <iostream>
#include <array>
template <typename Type>
struct Constant
{
constexpr Constant(const Type source) : _data({{source}}) {;}
constexpr Constant(const std::array<Type, 1> source) : _data(source) {;}
constexpr Constant<Type> operator()() const {return _data;}
constexpr operator Type() const {return _data[0];}
const std::array<Type, 1> _data;
static constexpr Constant<Type> pi = 3.1415926535897932384626433832795028841971693993751058209749445L;
};
int main(int argc, char* argv[])
{
std::cout<<Constant<double>::pi()<<std::endl;
return 0;
}
I get a compiler error with g++4.7.3
and g++4.8.0
(which is an undefined reference to pi
(sorry it's in French)) :
/tmp/cctdvPfq.o: dans la fonction « main »:
main.cpp:(.text.startup+0xd): référence indéfinie vers « Constant<double>::pi »
collect2: erreur: ld a retourné 1 code d'état d'exécution
As my system is a fresh install (first time with g++4.7.3
and g++4.8.0
), I do not know whether it comes from my system configuration or from the compiler. If it comes from the compiler, where is the problem ?
EDIT: and why this is working ? (version with no array)
#include <iostream>
#include <array>
template <typename Type>
struct Constant
{
constexpr Constant(const Type source) : _data(source) {;}
constexpr Constant<Type> operator()() const {return _data;}
constexpr operator Type() const {return _data;}
const Type _data;
static constexpr Constant<Type> pi = 3.1415926535897932384626433832795028841971693993751058209749445L;
};
int main(int argc, char* argv[])
{
std::cout<<Constant<double>::pi()<<std::endl;
return 0;
}
Upvotes: 1
Views: 207
Reputation: 126542
You could avoid invoking the call operator on pi
, so that it is not odr-used by your program, and the compiler can treat pi
just like a value that can be inlined, thus not requiring a definition for that static
data member:
std::cout << Constant<double>::pi << std::endl;
// ^^
Alternatively, you could keep invoking pi
's call operator, and provide a definition of your static data member at namespace scope, and use the call operator of Constant<double>
as in your original code:
template <typename Type>
struct Constant
{
// ...
static constexpr Constant<Type> pi = /* ... */;
};
template<typename Type>
constexpr Constant<Type> Constant<Type>::pi;
Per paragraph 9.4.2/3 of the C++11 Standard:
If a non-volatile
const
static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with theconstexpr
specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
Upvotes: 6