通过运行时索引访问派生 class 中基 classes 的成员

Accessing members of base classes in the derived class through runtime indexing

考虑以下代码

#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;
}

它创建了一个带有数据成员value 的模板化结构base,以及一个继承自其多个特化的结构derived。我想根据运行时提供的索引访问一个或另一个基数 class 的 value。提供的代码似乎没有实现这一点,总是 returns 第一个 basevalue

我想实现:

换句话说,我不想改变的代码布局应该是:

#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;
}

问题:为什么当前的技巧不起作用,有没有办法让它起作用?

编辑:似乎是 GCC 错误。我举报了 here. Comparison with clang here.

额外问题(语言律师):它是 GCC 错误,还是根据 C++17 标准未定义的行为?

这很可能是评论中指出的 GCC 错误。尽管如此,有一个有趣的解决方法,它使用 std::tuple 而不是 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;
}

这看起来像是 GCC 错误。您的原始代码 produces Clang 的预期输出。

我找到的 GCC 解决方法是将 members 变成静态成员函数:

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];
}