Reputation: 561
I am trying to use make_tuple()
in constexpr
. It works in global scope. But it generates link error for static constexpr
class member.
#include <iostream>
#include <tuple>
using namespace std;
class A
{
public:
static constexpr auto z = make_tuple(5, 3.0);
};
constexpr auto tp = make_tuple(6, 3.2);
int main()
{
cout << get<0>(tp) << " " << get<1>(tp) << endl; // OK
cout << get<0>(A::z) << " " << get<1>(A::z) << endl; // error: (.text+0x5a): undefined reference to `A::z'
// (.text+0x67): undefined reference to `A::z'
}
I have checked here make_tuple
is not itself a constexpr
in c++11
. I guess that is not the problem in this case. If it was it would generate a compile error instead of link error.
I have tried to define the constexpr
outside the class like bellow as suggested by this answer
class A
{
public:
static constexpr tuple<int, double> z;
};
constexpr tuple<int, double> A::z = make_tuple(5, 3.0);
But, it generates several compile error. That makes sanse according to answers of constexpr initializing static member using static function
What is the correct way to use make_tuple
in static constexpr
class member?
Compiler:
g++ 4.8.4
and clang 3.4
with -std=c++11
Upvotes: 2
Views: 1458
Reputation: 275415
In C++11/14, static constexpr
variables must be defined somewhere if ODR-used (ODR-used means "used in a way that requires it have an identity". As an example, take a real reference or pointer to them. ODR here means "one definition rule", and ODR-used means "used in a sense that requires it to have exactly one definition", which I shorten to "have an identity").
constexpr tuple<int, double> A::z;
and we have defined where it exists. This needs to be in exactly one compilation unit.
In C++17, inline
variables were added and I believe constexpr
variables were implicitly made inline. I'm not a C++17 expert, but it compiles and runs in C++1z mode without the definition, so my interpretation seems at least half correct.
(inline
variables, like inline
functions, have the definition in the end created wherever the compiler wants. inline
functions are typically implemented by having a definition in every object file with special notes when someone takes an address of it, then the duplicates are discarded at link time and everyone refers to the last one standing. I don't know if inline
variables tend to be implemented the same way or not.)
If you want to solve this problem in C++11 without having to put something in a .cpp
file with hardcoded template arguments, change z
to a constexpr
function and call it when you want to use it.
As SergyA has mentioned, ODR is triggered here because get
returns a reference to a field in the tuple in question, and that reference implies an identity must exist for the field and hence the entire tuple.
In theory, a carefully crafted value_get
that returned by value could avoid this ODR-usage, but it would depend on the implemenation of tuple
as it in turn could not call get
.
Upvotes: 5