在 C++ 中获取类型的底层位的通用且可移植的方法是什么?

What is a generic and portable way to get at underlying bits of a type in C++?

我想采用不大于指针的任意类型并获取其位表示形式。我尝试使用 std::bit_cast,但这需要两种类型的大小相同。但是,由于通用性,我不知道输入类型的大小。我写了这个函数:

template<class To, class From>
To sloppy_bit_cast(From x) {
  To temp{0};
  std::memcpy(&temp, &x, std::min(sizeof(From), sizeof(To)));
  return temp;
}

它在我的电脑上工作,但我担心 memcpy 调用会根据字节序有不同的行为。我还需要担心其他可移植性陷阱吗? std::bitset 的转换函数是否已经可移植地解决了这个问题?

编辑:具体来说,我想从最低有效位复制到最低有效位。

如果您有兴趣,这里是目前的全部代码(我还没有测试过,但我的 linter 接受了它,至少感觉是对的)。我不是要您阅读或评论它(尽管我很乐意听到反馈)。但我很高兴你们都帮助我解决了这个问题,所以我想分享。 我正在玩省略断言,不仅是为了了解我在那里的一般想法,而且我认为我可以解决这个问题来约束

#include <array>
#include <cstring>
#include <cstdint>


template<class T>
constexpr T narrow_cast(auto x) noexcept{
  return static_cast<T>(x);
}

template<class To, class From>
constexpr To bit_cast(From x) noexcept {
  static_assert(sizeof(To) == sizeof(From));
  To temp;
  std::memcpy(&temp, &x, sizeof(To));
  return temp;
}

template<class T>
constexpr auto bytes_of(T x) noexcept {
  return bit_cast<std::array<std::byte, sizeof x>>(x);
}

template<class T>
constexpr T to_integer(auto x) noexcept{
  return static_cast<T>(x);
}

template<class UInt, class T>
constexpr auto as_UInt(T x) noexcept {
  auto const bytes = bytes_of(x);
  UInt acc{};
  //endian agnostic
  for (auto i = 0u; i < bytes.size(); ++i) {
    auto const this_byte = to_integer<UInt>(bytes[i]);
    acc |= (this_byte << (i * 8u));
  }
  return acc;
}

template<class To, class UInt>
constexpr auto from_UInt(UInt from) noexcept {
  std::array<std::byte,sizeof(To)> bytes;
  //endian agnostic?
  for (auto i = 0u; i < bytes.size(); ++i){
    bytes[i] = narrow_cast<std::byte>((from >> (i * 8u)) & 0xFFu);
  }
  return bit_cast<To>(bytes);
}

template<class T>
constexpr auto as_uintptr_t(T x) noexcept {
  return as_UInt<uintptr_t>(x);
}

template<class To>
constexpr auto from_uintptr_t(uintptr_t x) noexcept {
  return from_UInt<To>(x);
}

template<class X, class Y, int low_bit_count_ = sizeof(Y) * 8>
class uintptr_pair {
  static_assert(sizeof(X) <= sizeof(uintptr_t));
  static_assert(sizeof(Y) <= sizeof(uintptr_t));

 public:
  static constexpr auto low_bit_count = low_bit_count_;
  static constexpr auto high_bit_count = sizeof(uintptr_t)*8 - low_bit_count;
  constexpr uintptr_pair() = default;
  constexpr uintptr_pair(X x, Y y) noexcept
      : x_{as_uintptr_t(x)}, y_{as_uintptr_t(y)} {}

  constexpr X x() const noexcept { return from_uintptr_t<X>(x_); }
  constexpr Y y() const noexcept { return from_uintptr_t<Y>(y_); }

 private:
  uintptr_t x_ : high_bit_count;
  uintptr_t y_ : low_bit_count;
};

constexpr auto test() {
  uintptr_pair<int, int> p{3, 4};
  return std::pair{p.x(),p.y()};
}

您可以轻而易举地安排正确的内存量:

template<class T>
auto get_bytes(const T &t) {
  std::array<std::byte,sizeof t> ret;
  std::memcpy(ret.data(),&t,sizeof t);
  return ret;
}

即使 T 不可平凡复制,这仍然有效,尽管您无法将字节放回 in a T。 (std::bit_cast<std::array<…>> 可能 可能 有效,但不能真正保证 class 的大小是正确的。)

如果您希望结果为 整数 ,您可以至少选择一个字节,然后通过 | 和 [=15 自己填充它=](它产生一个值以独立于字节序等问题进行分析)或 memcpy 从您如上所述填充的数组中进入它。同样,如果 T 的字节数较少,理论上不能保证行为,但在实践中肯定会起作用。