【问题标题】:Accessing members of base classes in the derived class through runtime indexing通过运行时索引访问派生类中基类的成员
【发布时间】:2020-05-08 08:33:12
【问题描述】:

考虑下面的代码

#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。我想根据运行时提供的索引访问一个或另一个基类的value。提供的代码似乎没有实现这一点,并且总是返回第一个basevalue

我想实现它:

  • 不改变base的代码
  • 不改变derivedbase继承的方式
  • 不改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;
}

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

编辑:似乎是 GCC 错误。我报告了它here。与clanghere比较。

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

【问题讨论】:

标签: c++ inheritance c++17 language-lawyer pointer-to-member


【解决方案1】:

这看起来像一个 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];
}

【讨论】:

    【解决方案2】:

    这很可能是 cmets 中提到的 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;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-16
      • 2017-09-30
      • 1970-01-01
      相关资源
      最近更新 更多