Получение "parent" `std::tuple` из указателей на"children"

struct Apple { };
struct Banana { };
struct Peach { };

using FruitTuple = std::tuple<Apple, Banana, Peach>;

template<typename TTuple, typename TItem>
TTuple& getParentTuple(TItem* mItemPtr)
{
    // <static assert that the tuple item types are unique>
    // ...?
}

int main()
{
    FruitTuple ft;

    // I know these pointers point to objects inside a `FruitTuple`...
    Apple* ptrApple{&std::get<0>(ft)};
    Banana* ptrBanana{&std::get<1>(ft)};
    Peach* ptrPeach{&std::get<2>(ft)};

    // ...is there a way to get the `FruitTuple` they belong to?
    auto& ftFromA(getParentTuple<FruitTuple>(ptrApple));
    auto& ftFromB(getParentTuple<FruitTuple>(ptrBanana));
    auto& ftFromP(getParentTuple<FruitTuple>(ptrPeach));

    assert(&ftFromA == &ftFromB);
    assert(&ftFromB == &ftFromP);
    assert(&ftFromA == &ftFromP);

    return 0;
}

Как может getParentTuple<TTuple, TItem> быть реализованным стандартным и не зависящим от архитектуры способом?

2 ответа

Решение

Невозможно.

Редактировать:

Я не думаю, что в стандарте есть что-то, что мешает реализации кортежей, соответствующих требованиям, распределять элементы по отдельности в куче.

Таким образом, расположение элементов в памяти не позволяет делать какие-либо выводы, которые приводят к расположению объекта кортежа.

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

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

В комментарии вы сказали, что вас не волнует этот случай, так что вот и вы:

template<typename TTuple, typename TItem>
TTuple& getParentTuple(TItem* mItemPtr)
{
    TTuple dummyTuple;

    // The std::get by type will not compile if types are duplicated, so
    // you do not need a static_assert.
    auto dummyElement = (uintptr_t)&std::get<TItem>(dummyTuple);

    // Calculate the offset of the element to the tuple base address.
    auto offset = dummyElement - (uintptr_t)&dummyTuple;

    // Subtract that offset from the passed element pointer.
    return *(TTuple*)((uintptr_t)mItemPtr - offset);
}

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

Другие вопросы по тегам