【问题标题】:Is it well-defined to use `std::addressof` on non-active union members [duplicate]在非活动工会成员上使用`std :: addressof`是否定义明确[重复]
【发布时间】:2018-09-21 09:25:06
【问题描述】:

下面的代码是在 C++11 中实现 offsetofconstexpr 版本的尝试。它可以在 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://wandbox.org/permlink/rKQXopsltQ51VtEm

这是石墨大师的原始版本: https://gist.github.com/graphitemaster/494f21190bb2c63c5516

【问题讨论】:

  • 所有成员都有相同的地址,无论是否活跃......即使输入的地址属于非活跃成员,我不明白为什么这么久会成为问题由于该地址没有被取消引用,原因在我建议的 [basic.life] 周围的欺骗中给出了原因。如果有人特别担心std::addressof(),那只是一堆演员杂技,不会取消引用或以其他方式读取对象。
  • [class.union] states 每个非静态数据成员都被分配,就好像它是 astruct 的唯一成员一样。联合对象的所有非静态数据成员都有相同的地址。 但我不确定这是否真的可以。
  • @underscore_d 我同意stackoverflow.com/questions/48188737/… 可能是部分答案,如果有信心断言答案,但事实并非如此。请注意,在这个问题中,我使用 . 浏览工会成员内容
  • 让我担心的是代码似乎假定该成员以其自身大小的倍数对齐。这对于一般的原始类型来说可能是正确的,但我可以想象在很多情况下 T1 是一个很容易打破这条规则的结构。

标签: c++ c++11 language-lawyer


【解决方案1】:
union U { int a; int b; };
U u;
u.a = 0; // (1)
int* pub = &u.b;

是的,这是很好的定义,但是对使用pub 的方式有限制。注意:使用运算符 &amp;std::addressof 获取对象的地址类似1,除非为该对象的类型定义了自定义运算符 &amp;

[class.union]/1
在联合中,如果一个非静态数据成员的名称引用了一个生命周期已经开始但尚未结束的对象,那么它就是活动的。

所以在标记为(1) 的行上,u.b 的生命周期还没有开始,但是对象将占用的存储空间已经分配。以下:

[basic.life]/6
在对象的生命周期开始之前但在对象将占用的存储空间分配之后,或者在对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前,表示对象地址的任何指针可以使用对象将要或曾经所在的存储位置,但只能以有限的方式使用。


1) 除了它会,正如用户 Quentin 所指出的,绑定对 u.b 的引用,但按照 [basic.life]/7 也可以:

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

【讨论】:

  • 您是否介意明确评论代码示例中使用std::addressof 的结果的方式是否定义明确?
  • 有一个细微的差别,std::addressof 将引用绑定到该非活动成员,并且必须初始化引用以引用实际对象。但我认为这也有特殊情况。
  • @Quentin 我不知道,谢谢。
  • sh*t 我是那个回答重复的人,我不记得了。
  • 别担心。有一次我试图否决我自己的答案之一......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-16
  • 1970-01-01
  • 1970-01-01
  • 2017-02-07
  • 2018-03-13
  • 1970-01-01
  • 2013-11-30
相关资源
最近更新 更多