2

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

10
  • works well for me. Commented Aug 2, 2017 at 16:34
  • not with a non-intrinsic object sadly. Use a static constexpr function to deliver the object. Commented Aug 2, 2017 at 16:35
  • what is the g++ version of this compiler? Commented Aug 2, 2017 at 16:36
  • wait.. this works in latest version of gcc and clang. Commented Aug 2, 2017 at 16:41
  • 1
    @user0042 This is an ODR issue. Turn off -O2 and it will no longer work. Commented Aug 2, 2017 at 16:42

1 Answer 1

5

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").

Simply add:

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I would just add that the ODR-usage here happens due to the fact that get<> returns a reference. I imagine, it should be possible to do my_get<> returning value, which would not trigger the problem.
defined, not declared. The implicit inline applies to static data members only.
@yakk Thanks for explaining how it violated ODR and suggestion to make it constexpr function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.