c++ - 在非事件联盟成员上使用 `std::addressof` 是否明确

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

<分区>

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

这取决于将 std::addressof 应用于 union 体的非活跃成员的成员。

这是定义明确的 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

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

最佳答案

union U { int a; int b; };
U u;
u.a = 0; // (1)
int* pub = &u.b;

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

[class.union]/1
In a union, a non-static data member is active if its name refers to an object whose lifetime has begun and has not ended.

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

[basic.life]/6
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.


1) 除了它会像用户 Quentin 指出的那样绑定(bind)对 u.b 的引用,但根据 [basic.life]/7 也可以。 :

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using the properties of the glvalue that do not depend on its value is well-defined.

关于c++ - 在非事件联盟成员上使用 `std::addressof` 是否明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49775980/

相关文章:

c++ - 如何创建用于添加两个矩阵的函数?它会返回什么?

c++ - 如何在 Visual Studio 中读取 Unresolved external symbol 错误?

c++ - 我正在尝试传递一个 lambda 作为参数

c++ - 如果对象没有复制构造函数,则 lambda 捕获失败(在 MSVC 上)

c++ - 通过接口(interface)完成 copy-and-swap

c++ - C++ 中的 median_unsafe 是什么意思

c++ - 到达流的 EOF 时,seekg() 不会设置 eofbit。是设计使然吗?

c++ - 将 std::future 和 std::promise 与非 std::thread 一起使用是否安全?

java - 非捕获 lambda 似乎仍然捕获了封闭的实例

C++标准歧义