Существует ли совместимый со стандартами способ определения выравнивания нестатического элемента? - PullRequest
2 голосов
/ 03 июля 2019

Предположим, у меня есть некоторая структура S и нестатический член member, как в этом примере:

struct S { alignas(alignof(void *)) char member[sizeof(void *)]; };

Как вы получаете выравнивание member?

Оператор alignof может применяться только к завершенным типам, но не к выражениям [ в 7.6.2.5.1 ], хотя GCC это позволяет, поэтому alignof(S::member) и Clang его поддерживают.

Что такое «языковой адвокат» стандартный способ сделать это без этого ограничения?

Кроме того, sizeof допускает аргументы выражений, есть ли причина асимметрии?

Практическая задача состоит в том, чтобы иметь возможность получить выравнивание элементов шаблонных структур, вы можете сделать decltype, чтобы получить их тип, sizeof, чтобы получить их размер, но тогда вам также потребуется выравнивание.

1 Ответ

0 голосов
/ 03 июля 2019

Выравнивание типа или переменной - это описание адресов памяти, которые может занимать переменная - адрес должен быть кратным выравниванию *. Однако для членов-данных адрес элемента-данных может быть любым K * alignof(S) + offsetof(S, member). Давайте определим выравнивание элемента данных как максимально возможное целое число E, чтобы &some_s.member всегда было кратным E.

Учитывая тип S с членом member, пусть A = alignof(S), O = offsetof(S, member).
Допустимые адреса S{}.member: V = K * A + O для некоторого целого числа K.
V = K * A + O = gcd(A, O) * (K * A / gcd(A, O) + O / gcd(A, O)).
Для случая, когда K = 1, других факторов не существует.
Таким образом, gcd(A, O) является лучшим фактором, действительным для неизвестных K.

Другими словами, "alignof(S.member)" == gcd(alignof(S), offsetof(S, member)).

Обратите внимание, что это выравнивание всегда является степенью двойки, поскольку alignof(S) всегда является степенью двойки.

*: В моем кратком наборе в стандарт я не смог найти эту гарантию, то есть адрес переменной может быть K * alignment + some_integer. Однако это не влияет на конечный результат.


Мы можем определить макрос для вычисления выравнивания элемента данных:

#include <cstddef> // for offsetof(...)
#include <numeric> // for std::gcd

// Must be a macro, as `offsetof` is a macro because the member name must be known
// at preprocessing time.
#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))

Это гарантировано только для стандартных типов макетов, поскольку offsetof гарантировано только для стандартных типов макетов . Если класс не является стандартным макетом, эта операция поддерживается условно.

Пример: * 1 047 *

#include <cstddef>
#include <numeric>

struct S1 { char foo; alignas(alignof(void *)) char member[sizeof(void *)]; };
struct S2 { char foo; char member[sizeof(void *)]; };

#define ALIGNOF_MEMBER(cls, member) (::std::gcd(alignof(cls), offsetof(cls, member)))

int f1() { return ALIGNOF_MEMBER(S1, member); } // returns alignof(void *) == 8
int f2() { return ALIGNOF_MEMBER(S1, foo); }    // returns 8*
int f3() { return ALIGNOF_MEMBER(S2, member); } // returns 1

// *: alignof(S1) == 8, so the `foo` member must always be at an alignment of 8

Проводник компилятора

...