簡體   English   中英

通過運行時索引訪問派生 class 中基類的成員

[英]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 提供的代碼似乎沒有實現這一點,並且總是返回第一個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 錯誤。 在這里報告了。 在此處與 clang 進行比較。

額外的問題(語言律師) :它是 GCC 錯誤,還是根據 C++17 標准的未定義行為?

這看起來像一個 GCC 錯誤。 您的原始代碼生成預期的 output 和 Clang。

我能夠找到的 GCC 的一種解決方法是將members變成 static 成員 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];
}

如評論中所述,很可能是 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM