C ++: Получить адрес полного объекта, содержащего подобъект члена - PullRequest
0 голосов
/ 28 мая 2018

У меня есть два класса, которые примерно определены следующим образом:

class Inner {
public:
  bool is_first;
};

class Outer {
public:
  char some_other_member;
  Inner first;
  Inner second;
}

Я знаю, что Inner s всегда живет только внутри Outer s, и что соответствующий флаг bool будет установлен на true если и только если соответствующий объект является членом first, а не second.

Я ищу совместимый со стандартом способ получения указателя на объект Outer, содержащий некоторые Inner объект.Конечно, я мог бы просто хранить указатель внутри каждого Inner объекта, но поскольку класс Inner очень мал и у меня есть лотов , это кажется пустой тратой памяти (и, следовательно, драгоценного кэша)).

Очевидно, что компилятор должен знать смещение памяти между first, second и содержащим Outer объектом.Вопрос заключается в следующем: существует ли совместимый со стандартом способ сказать компилятору «получить это смещение, вычесть его из указателя в Inner и сделать его указателем Outer»?

Я знаю, что мог бы использоватьприведение к void, если Outer будет содержать Inner s в качестве базовых подобъектов (например, this ) - я очень чувствую, что нечто подобное должно быть возможным для подобъектов-членов?

Ответы [ 3 ]

0 голосов
/ 28 мая 2018

Ни одно решение не является строго правильным, вы сталкиваетесь с некоторыми правилами арифметики указателей .Однако существует решение, определяемое реализацией, которое почти всегда делает то, что от него ожидают

auto get_outer(Inner& i)
{
    return (Inner*)(((uintptr_t)&i) - offsetof(Outer, first));
}

Улов, являющийся указателем, и преобразования целочисленных типов определяются реализацией.

0 голосов
/ 28 мая 2018

Следует отметить, что проблема получения указателя на родительский объект из указателя на член, как правило, не разрешима.

offsetof и приведение работает только для стандартные типы макетов .В других случаях это неопределенное поведение.

Сбой, например, для множественного виртуального наследования.В этом случае доступ к элементу реализуется более сложным способом, чем добавление смещения времени компиляции.Возможно, это практически не так важно, так как используется очень редко, но вы явно просили о соответствии стандартам.

0 голосов
/ 28 мая 2018

Вот для чего offsetof.

Что-то вроде:

Outer *Inner::getOuter() {
    size_t offset = flag ? offsetof(Outer, first) : offsetof(Outer, second);
    return reinterpret_cast<Outer *>(
        reinterpret_cast<char *>(this) - offset);
}

Обратите внимание, что вы reinterpret_casting, так что вы полностью ответственны, чтобы знать, что выделают.Если флаги выйдут из синхронизации, вы получите неопределенное поведение.

Обратите внимание, что арифметика указателей разрешена только внутри массива, но в §6.9 / 4 ([basic.types]) явно говорится, что каждый объектхранится в таком массиве unsigned char:

представление объекта объекта типа T представляет собой последовательность N unsigned charобъекты, занятые объектом типа T, где N равно sizeof(T).

и в этом массиве определена арифметика указателя.Но вы должны быть уверены, что вы действительно в пределах распределения.

...