【问题标题】:Getting "parent" `std::tuple` from "children" item pointers从“子”项指针获取“父”`std::tuple`
【发布时间】:2015-06-02 13:18:20
【问题描述】:
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&lt;TTuple, TItem&gt; 如何以标准兼容不依赖架构的方式实现?

【问题讨论】:

  • 可以假设你的元组不包含多个相同类型的实例吗?
  • 我不认为 std::stuple 给你足够的内存布局保证来做到这一点是一种独立于实现的方式。
  • 您是否可以从结构的子对象(例如标准布局)转到封闭的结构对象已经是个问题了。虽然诸如 C++ 对象模型、标准布局保证和offsetof 之类的东西强烈暗示你可以,但标准只是害羞地说你可以这样做,例如对对象的底层字节执行指针算术,这是您绝对需要的。

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


【解决方案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);
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 2018-06-10
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多