Указатели на массивы элементов с различными размерами - PullRequest
4 голосов
/ 04 октября 2019

Предположим, у меня есть класс с двумя элементами-массивами одного и того же типа элемента, но разных размеров:

struct X
{
  string a[2];
  string b[3];
};

constexpr auto array_members = std::array{ &X::a, &X::b };

Это не компилируется, поскольку два массива (различной длины) имеют несовместимые типы.

Существует ли общий тип, которому могут быть назначены оба указателя члена?

Я также пытался static_cast<string (X::*)[]>(&X::a), но он также не компилируется из-за неполного типа массива.

Я не могу использовать offsetof, потому что он должен работать с классами нестандартной компоновки.

Это может быть обходной путь:

using array_type = decltype(X::b);
auto const array_members = std::array{
  reinterpret_cast<array_type X::*>(&X::a), // cannot be constexpr
  &X::b
};

, но я обеспокоен вызовом неопределенного поведения. Несмотря на то, что я уверен, что во время выполнения отсутствуют ссылки на внешние элементы, мы создаем указатели «один за другим» на a, что с учетом типа b выглядит как ограниченное. ,Я не уверен, идет ли это вразрез со спецификацией. Также было бы удобно, если бы я мог использовать constexpr, который несовместим с reinterpret_cast.

Ответы [ 2 ]

5 голосов
/ 04 октября 2019

У вас не может быть массива указателей на элементы разных типов.

Вместо указателя на член вы могли бы вместо этого использовать какую-то функцию, которая возвращает span для члена:

template<auto m>
constexpr auto getter = [](X& x) noexcept
                        // X could be deduced from m for extra genericity
                        // https://stackoverflow.com/questions/25228958
{

    return span{x.*m, std::size(x.*m)};
};

constexpr auto array_members = std::array{ +getter<&X::a>, +getter<&X::b> };

Сборка не генерируется:) при нулевом уровне оптимизации (пока вы, конечно, не вызовете функции). array_members - это массив указателей на функции. Пример использования:

X x;
span a = array_members[0](x);
a[0] = "test";

Используется span, которого нет в стандартной библиотеке C ++ 17, поэтому вам нужно использовать другую его реализацию.

0 голосов
/ 04 октября 2019

Вы можете использовать std :: variable и std :: array , чтобы достичь желаемого:

#include <variant>
#include <array>

struct X
{
    std::variant<array<string, 2>, array<string, 3>> a;
    std::variant<array<string, 2>, array<string, 3>> b;

};

 constexpr auto array_members = std::array{ &X::a, &X::b };
 array<string, 3> arr;
 std::variant<array<string, 2>, array<string, 3>> a = arr;
 cout<<"test array: "<<std::get<1>(a)[0];
 //cout<<std::get<0>(a)[0]; will throw 

Недостатком является то, что оба aи b будет занимать память столько же, сколько array<string, 3>. std::variant - это безопасный тип.

Live on godbolt

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...