5

I have the following code:

#include <iostream>
#include <tuple>

class T
{
    public:
        using Names = std::tuple<char const*, char const*>;
        static constexpr Names names {"First", "Second"};
};

int main()
{
    std::cout << std::get<0>(T::names);
}

As names is a constexpr I expected this to work. But I get a linker error:

The compiler:

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.0.0
Thread model: posix

The error:

> g++ -std=c++1y pl.cpp
Undefined symbols for architecture x86_64:
  "T::names", referenced from:
      _main in pl-377031.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

[live demo]

Martin York
  • 257,169
  • 86
  • 333
  • 562

2 Answers2

7

A declaration of a static data member in class is never a definition1.
A definition is necessary whenever a variable is odr-used2. std::get<> takes arguments per reference, and binding a variable to a reference odr-uses it immediately3.

Simply define names outside:

constexpr T::Names T::names; // Edit: This goes *outside* the class "as is"!

Demo.


1) [basic.def]/2:

A declaration is a definition unless [..] it declares a static data member in a class definition (9.2, 9.4)

2) [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.

3) According to [basic.def.odr]/3:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause 5).

Here the id-expression T::names refers to the variable in question. The only superexpression e that contains all the potential results of T::names is T::names itself, because the set of potential results of a function call, i.e. std::get<0>(T::names), is empty. However, the lvalue-to-rvalue conversion is clearly not applied, and the value of T::names is also clearly not discarded (as it is passed to a function).
Thus it is odr-used and requires a definition.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • I assume that `constexpr T::Names T::names;` must go in a compilation unit and can not be in a header file included multiple times. – Martin York Jan 25 '15 at 18:12
  • @CrappyExperienceBye Correct - IIRC `T::names` has external linkage. – Columbo Jan 25 '15 at 18:20
  • I have a hard time understanding odr-use because of the terminology involved: I've read about potentially-evaluated expressions, lvalue-to-rvalue conversion, and potential results in the Standard and on cppreference but it's confusing. Is there a comprehensive source that explains odr-use in English? – template boy Jan 25 '15 at 18:42
  • @templateboy http://stackoverflow.com/questions/19630570/what-does-it-mean-to-odr-use-something – Columbo Jan 25 '15 at 18:53
2

@Columbo posted the correct solution.

Unfortunately I am trying to build a header only library. The solution requires that the static member be compiled into one compilation unit (which is what I was using constexpr in the hopes of avoiding).

So I needed to stick another twist into the works to make it work. This is just to share my solution:

#include <iostream>

class T
{
    public:
        using Names = std::tuple<char const*, char const*>;
        template<std::size_t index>
        static char const* getName()
        {
            static constexpr Names names {"First", "Second"};
            return std::get<index>(names);
        }
};

int main()
{
    std::cout << T::getName<0>() << "\n";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562