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];
}
It is most probably GCC bug as noted in comments. Nonetheless there is funny workaround for this which using std::tuple
instead of std::array
:
#include <tuple>
#include <array>
#include <iostream>
template <std::size_t> struct base {
std::size_t value;
};
template<class T, class Tuple, std::size_t... Indx>
constexpr auto to_arr_h(const T& val, const Tuple& t, std::index_sequence<Indx...>) {
return std::array<std::size_t, sizeof...(Indx)>{val.*(std::get<Indx>(t))...};
}
template<class T, class... Ts>
constexpr auto to_arr(const T& val, const std::tuple<Ts...>& t) {
return to_arr_h(val, t, std::make_index_sequence<sizeof...(Ts)>{});
}
struct derived: base<0>, base<1> {
static constexpr auto members = std::make_tuple(&base<0>::value, &base<1>::value);
constexpr std::size_t& operator[](std::size_t i) noexcept {
return to_arr(*this, members)[i];
}
constexpr const std::size_t& operator[](std::size_t i) const noexcept {
return to_arr(*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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.