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>
быть реализован в Стандарт-совместимый а также не-зависит от архитектуры путь?
Невозможно.
Редактировать:
Я не думаю, что в стандарте есть что-то, что мешает реализации кортежей, отвечающих требованиям, распределять элементы по отдельности в куче.
Расположение в памяти элементов, таким образом, не допускает никаких выводов, которые приводят к расположению объекта кортежа.
Единственное, что вы можете сделать, это расширить ваши классы элементов, чтобы они также содержали обратный указатель на кортеж, который вы затем заполняете после помещения ваших элементов в кортеж.
Ниже приведен код, который должен работать с распространенными реализациями, но я вполне уверен, что он не соответствует стандарту, поскольку он предполагает, что структура памяти кортежа является определяющей.
В комментарии вы сказали, что вас не волнует этот случай, так что вот и вы:
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);
}
Обратите внимание, что это создает кортеж один раз, что может иметь нежелательные побочные эффекты или влияние на производительность в некоторых случаях. Я не уверен, есть ли вариант времени компиляции этого.