Consider the following code
#include <array>
#include <iostream>
template <std::size_t> struct base {
std::size_t value;
};
struct derived: base<0>, base<1> {
using pointer_type = std::size_t derived::*;
static constexpr std::array<pointer_type, 2> members{{
&derived::base<0>::value,
&derived::base<1>::value
}};
constexpr std::size_t& operator[](std::size_t i) noexcept {
return this->*(members[i]);
}
constexpr const std::size_t& operator[](std::size_t i) const noexcept {
return this->*(members[i]);
}
};
int main(int, char**) {
derived x{42, 84};
std::cout << sizeof(base<0>) + sizeof(base<1>) << " " << sizeof(derived);
std::cout << std::endl;
std::cout << x[0] << " " << x[1];
std::cout << std::endl;
return 0;
}
It creates a templated structure base
with a data member value
, and a structure derived
that inherits from several specializations of it. I would like to access the value
of one or the other base class depending on an index provided at runtime. The provided code does not seem to achieve this, and always returns the value
of the first base
.
I would like to achieve it:
base
derived
inherits from the base
sizeof(derived)
(tricks should be constexpr/static
)reinterpret_cast
for example)O(1)
complexityIn other words, the layout of the code that I would like to not change should be:
#include <array>
#include <iostream>
template <std::size_t> struct base {
std::size_t value;
};
struct derived: base<0>, base<1> {
/* things can be added here */
constexpr std::size_t& operator[](std::size_t i) noexcept {
/* things can be added here */
}
constexpr const std::size_t& operator[](std::size_t i) const noexcept {
/* things can be added here */
}
};
int main(int, char**) {
derived x{42, 84};
std::cout << sizeof(base<0>) + sizeof(base<1>) << " " << sizeof(derived);
std::cout << std::endl;
std::cout << x[0] << " " << x[1];
std::cout << std::endl;
return 0;
}
QUESTION: Why is the current trick not working, and is there a way to make it work?
EDIT: Seems to be a GCC bug. I reported it here. Comparison with clang here.
EXTRA QUESTION (to language lawyers): Is it a GCC bug, or is it undefined behavior according to the C++17 standard?
This looks like a GCC bug. Your original code produces the expected output with Clang.
One workaround for GCC that I was able to find is to turn members
into a static member function:
static constexpr array_type members() noexcept {
return {&base<0>::value, &base<1>::value};
}
constexpr std::size_t& operator[](std::size_t i) noexcept {
return this->*members()[i];
}