获得"父母" 'std :: tuple`来自"孩子"项目指针

时间:2015-03-29 15:03:20

标签: c++ pointers tuples c++14 memory-layout

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 个答案:

答案 0 :(得分:3)

不可能。

编辑:

我认为标准中没有任何内容阻止兼容的元组实现在堆上单独分配元素。

元素的内存位置不允许任何推断导致元组对象的位置。

你唯一可以做的就是扩展你的元素类,使其也包含一个指向元组的后向指针,然后在将元素放入元组后将其填入。

答案 1 :(得分: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);
}

请注意,这会构造元组一次,在某些情况下可能会产生不必要的副作用或性能影响。我不确定是否有编译时变体。