Vincent
Vincent

Reputation: 60461

Undefined reference with g++4.7.3 and g++4.8?

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

Answers (1)

Andy Prowl
Andy Prowl

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 the constexpr 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

Related Questions