I have the following code:
#include
#include
class T
{
public:
using Names = std::tuple
@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";
}
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 expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.19) that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (4.1) is applied toe
, ore
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.