【发布时间】:2018-09-21 09:25:06
【问题描述】:
下面的代码是在 C++11 中实现 offsetof 的 constexpr 版本的尝试。它可以在 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