Question

I have wrote the following code to get the offset of a tuple element

template<size_t Idx,class T>
 constexpr size_t tuple_element_offset() {
        return static_cast<size_t>(
                    reinterpret_cast<char*>(&std::get<Idx>(*reinterpret_cast<T*>(0))) - reinterpret_cast<char*>(0));
    }

This is actually similar to the implementation of the offsetof macro. It looks ugly, but compiles and works fine on gcc-4.6

typedef std::tuple<int,char,long> mytuple;

mytuple var = std::make_tuple(4,'c',1000);
char * ptr = reinterpret_cast<char*>(&var);
long * pt = reinterpret_cast<long*>(ptr+tuple_element_offset<2,mytuple>());

std::cout << *pt << std::endl;

prints "1000".

I don't know too much about constexpr, so my questions are:

  1. Is it legal c++?
  2. More important, why I am allowed to call std::get (which is non constexpr) inside a constexpr function?

As far as I understand constexpr, the compiler is forced to evaluate the result of the expression at compile time, so no zero-dereferentiation can occurs in practice.

Was it helpful?

Solution

Is it legal C++?

If by "legal" you mean "well-formed," then, yes.

If by "legal" you mean "valid and will work on any compiler and Standard Library implementation, then, no, because std::tuple is not POD.

Why I am allowed to call std::get (which is not constexpr) inside a constexpr function?

Basically, a constexpr function doesn't necessarily have to consist of just a constant expression. If you tried to use your tuple_element_offset() function in a constant expression, you'd get a compilation error.

The idea is that a function might be usable in a constant expression in some circumstances but not in others, so there isn't a restriction that a constexpr function must always be usable in a constant expression (since there isn't such a restriction, it's also possible that a particular constexpr function might never be usable in a constant expression, as is the case with your function).

The C++0x draft has a good example (from 5.19/2):

constexpr const int* addr(const int& ir) { return &ir; } // OK

// OK: (const int*)&(const int&)x is an address contant expression
static const int x = 5;
constexpr const int* xp = addr(x); 

// Error, initializer for constexpr variable not a constant expression; 
// (const int*)&(const int&)5 is not a constant expression because it takes
// the address of a temporary
constexpr const int* tp = addr(5);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top