Доступ к членам базовых классов в производном классе через индексацию во время выполнения - PullRequest
3 голосов
/ 08 мая 2020

Рассмотрим следующий код

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

Он создает шаблонную структуру base с элементом данных value и структуру derived, которая наследуется от нескольких его специализаций. Я хотел бы получить доступ к value одного или другого базового класса в зависимости от индекса, предоставленного во время выполнения. Предоставленный код, похоже, не обеспечивает этого и всегда возвращает value первого base.

Я бы хотел добиться этого:

  • Без изменения кода из base
  • Без изменения способа derived наследуется от base
  • Без изменения sizeof(derived) (трюки должны быть constexpr/static)
  • Только с использованием определенного поведения (например, без неопределенного поведения reinterpret_cast)
  • Доступ должен быть O(1) сложностью

Другими словами, макет кода, который я хотел бы чтобы не менять, должно быть:

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

ВОПРОС : Почему текущий трюк не работает, и есть ли способ заставить его работать?

РЕДАКТИРОВАТЬ : похоже, ошибка G CC. Я сообщил об этом здесь . Сравнение с clang здесь .

ДОПОЛНИТЕЛЬНЫЙ ВОПРОС (к языковым юристам) : это ошибка G CC или неопределенное поведение в соответствии с C + +17 стандарт?

Ответы [ 2 ]

3 голосов
/ 08 мая 2020

Это похоже на ошибку G CC. Ваш исходный код дает ожидаемый результат с Clang.

Один обходной путь для G CC, который мне удалось найти, - это превратить members в функцию-член stati c:

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];
}
0 голосов
/ 08 мая 2020

Скорее всего, это ошибка G CC, как указано в комментариях. Тем не менее, для этого есть забавный обходной путь, который использует 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...