C++ 对非活跃工会成员使用“std::addressof”是否定义明确

C++ 对非活跃工会成员使用“std::addressof”是否定义明确,c++,c++11,language-lawyer,C++,C++11,Language Lawyer,下面的代码试图在C++11中实现offsetof的constepr版本。它在gcc 7.2.0和clang 5.0.0中编译 这取决于将std::addressof应用于工会非活跃成员的成员 这是定义良好的C++11吗?如果没有,请解释原因,包括引用本标准相关章节 #include <iostream> #include <cstdint> #include <memory> // based on the gist at: https://gist.git

下面的代码试图在C++11中实现
offsetof
constepr
版本。它在gcc 7.2.0和clang 5.0.0中编译

这取决于将
std::addressof
应用于工会非活跃成员的成员

这是定义良好的C++11吗?如果没有,请解释原因,包括引用本标准相关章节

#include <iostream>
#include <cstdint>
#include <memory>

// based on the gist at: https://gist.github.com/graphitemaster/494f21190bb2c63c5516
// original version by graphitemaster

template <typename T1, typename T2>
struct offset_of_impl {
    union U {
        char c;
        T1 m; // instance of type of member
        T2 object;
        constexpr U() : c(0) {} // make c the active member
    };
    static constexpr U u = {};

    static constexpr std::ptrdiff_t offset(T1 T2::*member) {
        // The following avoids use of reinterpret_cast, so is constexpr.
        // The subtraction gives the correct offset because the union layout rules guarantee that all
        // union members have the same starting address.
        // On the other hand, it will break if object.*member is not aligned.
        // Possible problem: it uses std::addressof on non-active union members.
        // Please let us know at the gist if this is defined or undefined behavior.
        return (std::addressof(offset_of_impl<T1, T2>::u.object.*member) - 
            std::addressof(offset_of_impl<T1, T2>::u.m)) * sizeof(T1);
    }
};

template <typename T1, typename T2>
constexpr typename offset_of_impl<T1, T2>::U offset_of_impl<T1, T2>::u;

template <typename T1, typename T2>
inline constexpr std::ptrdiff_t offset_of(T1 T2::*member) {
    return offset_of_impl<T1, T2>::offset(member);
}

struct S {
    S(int a_, int b_, int c_) : a(a_), b(b_), c(c_) {}
    S() = delete;
    int a;
    int b;
    int c;
};

int main()
{
    std::cout << offset_of(&S::b);   
}
#包括
#包括
#包括
//根据以下要点:https://gist.github.com/graphitemaster/494f21190bb2c63c5516
//graphitemaster的原始版本
模板
结构偏移量{
联合大学{
字符c;
T1 m;//成员类型的实例
T2对象;
constexpr U():c(0){}//使c成为活动成员
};
静态constexpr U={};
静态constexpr std::ptrdiff_t偏移量(T1 T2::*成员){
//以下内容避免使用reinterpret_cast,constexpr也是如此。
//减法给出了正确的偏移,因为联合布局规则保证所有
//工会成员的起始地址相同。
//另一方面,如果object.*成员未对齐,它将断开。
//可能的问题:它在非活动的联合成员上使用std::addressof。
//请让我们知道在要点,如果这是定义或未定义的行为。
return(std::addressof(offset\u of_impl::u.object.*成员)——
std::addressof(offset_of_impl::u.m))*sizeof(T1);
}
};
模板
constexpr typename offset_of_impl::U offset_of_impl::U;
模板
内联constepr std::ptrdiff t偏移量(T1 T2::*成员){
返回\u impl::offset(成员)的offset\u;
}
结构{
S(inta_,intb_,intc_):a(a_),b(b_),c(c_){
S()=删除;
INTA;
int b;
INTC;
};
int main()
{
标准::cout
是的,这是一个很好的定义,但是对使用
pub
的方式有限制。注意:使用操作符
&
std::addressof
获取对象的地址是相似的,除非为该对象的类型定义了自定义操作符
&


在联合中,如果非静态数据成员的名称引用的对象的生存期已开始,但尚未结束,则该成员处于活动状态

因此,在标记为
(1)
的行上,
u.b
的生存期尚未开始,但对象将占用的存储已分配。如下所示:


在对象的生存期开始之前,但在分配对象将占用的存储之后,或者在对象的生存期结束之后,在重用或释放对象占用的存储之前,可以使用表示对象将要或曾经所在的存储位置的地址的任何指针但只有有限的方式


1) 除此之外,正如用户Quentin所指出的,它将绑定一个对
u.b
的引用,但它也可以根据:

类似地,在对象的生存期开始之前,但在分配对象将占用的存储之后,或者在对象的生存期结束之后,在重用或释放对象占用的存储之前,可以使用引用原始对象的任何glvalue,但只能以有限的方式使用。对于对象er构造或销毁,请参见
[class.cdtor]
。否则,此类glvalue指的是已分配的存储([basic.stc.dynamic.allocation]),并且使用不依赖于其值的glvalue属性是明确定义的


所有成员都有相同的地址,无论是活动的还是非活动的……即使键入的地址属于非活动的成员,我也不明白为什么只要该地址没有被取消引用,这将是一个问题,原因在我建议的dupe around
[basic.life]
中给出。如果有人担心
std::addressof()
具体来说,它只是一组演员特技,不会取消引用或以其他方式读取对象。
[class.union]
说明每个非静态数据成员的分配方式与astruct的唯一成员相同。union对象的所有非静态数据成员都有相同的地址。但我不确定这是否真的可以。@下划线\u我同意可能是一个部分答案,如果答案是有把握地断言的,而不是。请注意,在这里问题:我正在使用
@RossBencina浏览工会成员的内容,但仅仅是为了获取其成员的地址,该地址也不存在,但需要遵守与工会自身地址相同的有限津贴。令我担心的是,该代码似乎假定该成员是以倍数排列的它自己的大小。对于基本类型来说,这可能是正确的,但我可以想象很多情况下T1是一个结构,这条规则很容易被打破。你介意明确地评论一下代码示例中使用
std::addressof
的结果的方式是否定义得很好吗?这与
st有细微的区别d::addressof
将引用绑定到该非活动成员,引用必须初始化以引用实际对象。但我认为这也有特殊的大小写。@昆汀我不知道,谢谢。我不是回答重复的那个人,我对它没有记忆。别担心。有一次我试图否决我自己的一个answ呃。。。
union U { int a; int b; };
U u;
u.a = 0; // (1)
int* pub = &u.b;