C++ 从指向某个成员的指针获取指向对象的指针

C++ 从指向某个成员的指针获取指向对象的指针,c++,pointers,c++11,language-lawyer,C++,Pointers,C++11,Language Lawyer,假设有一个结构 struct Thing { int a; bool b; }; 我得到一个指向该结构的成员b的指针,比如说作为某个函数的参数: void some_function (bool * ptr) { Thing * thing = /* ?? */; } 如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,我想要标准定义的行为,而不是未定义或实现定义的行为 作为旁注:我知道这规避了类型安全 void some_function (bool *

假设有一个结构

struct Thing {
  int a;
  bool b;
};
我得到一个指向该结构的成员
b
的指针,比如说作为某个函数的参数:

void some_function (bool * ptr) {
  Thing * thing = /* ?? */;
}
如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,我想要标准定义的行为,而不是未定义或实现定义的行为

作为旁注:我知道这规避了类型安全

void some_function (bool * ptr) {
  Thing * thing = (Thing*)(((char*)ptr) - offsetof(Thing,b));
}

我认为没有UB。

如果您确定指针确实指向结构中的成员
b
,就像有人这样做一样

Thing t;
some_function(&t.b);
然后,您应该能够使用宏获取指向结构的指针:

std::size_t offset = offsetof(Thing, b);
Thing* thing = reinterpret_cast<Thing*>(reinterpret_cast<char*>(ptr) - offset);
std::size\u t offset=offset of(Thing,b);
事物*事物=重新解释投射(重新解释投射(ptr)-偏移);
请注意,如果指针
ptr
实际上没有指向
Thing::b
成员,那么如果使用指针
Thing

X*get\u ptr(bool*b),上述代码将导致未定义的行为{
X* get_ptr(bool* b){
    static typename std::aligned_storage<sizeof(X),alignof(X)>::type buffer;

    X* p=static_cast<X*>(static_cast<void*>(&buffer));
    ptrdiff_t const offset=static_cast<char*>(static_cast<void*>(&p->b))-static_cast<char*>(static_cast<void*>(&buffer));
    return static_cast<X*>(static_cast<void*>(static_cast<char*>(static_cast<void*>(b))-offset));
}
静态typename std::aligned_storage::type buffer; X*p=静态_转换(静态_转换(&buffer)); ptrdiff_t const offset=static_cast(static_cast(&p->b))-static_cast(static_cast(&buffer)); 返回static_cast(static_cast(static_cast(b))-offset); }
首先,我们创建一些可以保存
X
的静态存储。然后我们得到缓冲区中可能存在的
X
对象的地址,以及该对象的
b
元素的地址


回到
char*
,我们可以得到缓冲区内
bool
的偏移量,然后我们可以使用它将指向实
bool
的指针调整回指向包含
X

的指针。我的建议源自@Rod answer in,类似于@0xbadf00d的one in

我开始想象一种偏移量的形式,它驱动指向类数据成员的指针的实现,后来这篇文章和我所做的测试证实了这一点

<>我不是C++实践者,很抱歉,简洁。

#include <iostream>
#include <cstddef>

using namespace std;

struct Thing {
    int a;
    bool b;
};

template<class T, typename U>
std::ptrdiff_t member_offset(U T::* mem)
{
    return 
    ( &reinterpret_cast<const char&>( 
        reinterpret_cast<const T*>( 1 )->*mem ) 
      - reinterpret_cast<const char*>( 1 )      );
}

template<class T, typename U>
T* get_T_from_data_member_pointer (U * ptr, U T::*pU) {
  return reinterpret_cast<T*> (
      reinterpret_cast<char*>(ptr) 
    - member_offset(pU));
}

int main()
{

    Thing thing;
    thing.b = false;

    bool * ptr = &thing.b;
    bool Thing::*pb = &Thing::b;

    std::cout << "Thing object address accessed from Thing test object lvalue; value is: " 
        << &thing << "!\n";     
    std::cout << "Thing object address derived from pointer to class member; value is: " 
        << get_T_from_data_member_pointer(ptr, &Thing::b) << "!\n";    
}
#包括
#包括
使用名称空间std;
结构物{
INTA;
布尔b;
};
模板
std::ptrdiff\U t成员\U偏移量(U t::*mem)
{
返回
(&reinterpret_cast(
重新解释(1)->*mem)
-重新解释(一),;
}
模板
T*从数据成员指针获取数据(U*ptr,U T::*pU){
返回重新解释(
重新解释铸件(ptr)
-成员(pU);;
}
int main()
{
事物;
事物b=虚假;
bool*ptr=&thing.b;
布尔事物::*pb=&事物::b;

std::从
bool
地址中扣除
Thing
指针不是很难吗?为什么不把Thing地址/指针传递给函数?@Stefan它应该是分配器的一部分:它有带簿记和数据的节点,我在分配时给数据一个指针,然后在释放时得到它。我刚刚注意到你了r在分配程序中对此有何评论。这意味着您不能保证它是标准布局,而且
offsetof
可以是未定义的。您可能从2015年11月起就开始了这一过程,但如果它仍然相关,并且出现了一个非
offsetof
的答案,我建议您实现它。幸运的是,对于分配程序,您可以不需要返回指针。您可以将满足
NullablePointer
RandomAccessIterator
typedef
的任何类似指针的结构返回到
pointer
。只是想知道:如果您需要从某个成员处获取对象,您不能简单地将对象作为指针传递的原因是什么成员的ead?AFAIK
char*
对于这种指针算法来说是完全安全和规范的。好吧,这里太混乱了:)char不能小于8位,因为最小范围,如果它大于8位,
int8\u t
甚至不可能存在,因为地址和大小都很好。“这就是使用
int8_t
的实际意义,它被定义为
char
可能不是的字节”-不,你完全弄错了。在这里使用
char
int8_t
是错误的。还要注意,
offsetof
对于非标准布局类型有未定义的行为(C++11)在实践中,它通常仍能按预期工作。术语“标准布局”大约是指“不使用C所没有的任何功能的东西”原来的代码< >偏移实际上是C特征,而不是C++,而C++规范明确地表示它在不标准布局的类上是不定义的。参见CPUPACTY。您是指声明“代码>缓冲区< /代码>为<代码>静态类型名STD::AligNeDySturt::Type < /代码>?是否有理由<代码> STA?tic\u cast
通过
void*
而不是简单地
重新解释\u cast
直接到
char*
X*
?是的,很好。我不喜欢
重新解释\u cast
,因为映射没有明确规定。
静态\u cast
有一个定义的映射。我想这在一般情况下是有意义的,尽管
重新解释指针的et_cast
定义得很好。我不喜欢这样保留额外的空间,但在其他方面它似乎很好。我非常担心这个
X
和参数1之间的偏移量不一样,但是如果编译器这样做的话(对于非标准布局,我找不到任何证据,但我怀疑是否有证据),我不确定有什么办法可以绕过它。很抱歉,我昨天没有及时赶到这里分配全部奖金;这似乎仍然是最好的解决方案。虽然我确实喜欢@rfb为它制作了一个通用函数,但由于对齐问题,我不确定它是否符合标准。很抱歉,如果我的答案有价值,我认为它是not用于泛型,而是因为它提出了