什么特征/概念可以保证对象的 memsetting 定义明确?

What trait / concept can guarantee memsetting an object is well defined?

假设我定义了一个 zero_initialize() 函数:

template<class T>
T zero_initialize()
{
    T result;
    std::memset(&result, 0, sizeof(result));
    return result;
}

// usage: auto data = zero_initialize<Data>();

为某些类型调用 zero_initialize() 会导致未定义的行为1,2。我目前正在强制 T 验证 std::is_pod。随着该特性在 C++20 中被弃用以及概念的出现,我很好奇 zero_initialize() 应该如何发展。

  1. 什么(最小)特征/概念可以保证对对象进行内存设置是明确定义的?
  2. 我应该使用 std::uninitialized_fill 而不是 std::memset 吗?为什么?
  3. 此函数是否已被类型子集的 C++ 初始化语法之一废弃?还是随着未来 C++ 版本的推出?

1) Erase all members of a class.
2)What would be reason for “undefined behaviors” upon using memset on library class(std::string)? [closed]

What (minimal) trait / concept can guarantee memsetting an object is well defined?

根据 std::memset reference on cppreference memset 在非 TriviallyCopyable 类型上的行为未定义。因此,如果可以 memset 一个 TriviallyCopyable 那么你可以添加一个 static_assert 到你的 class 来检查

template<class T>
T zero_initialize()
{
    static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
    T result;
    std::memset(&result, 0, sizeof(result));
    return result;
}

这里我们使用 std::is_trivial_v 来确保 class 不仅可以平凡复制,而且它还有一个平凡的默认构造函数,所以我们知道零初始化是安全的。

Should I use std::uninitialized_fill instead of std::memset? And why?

你不需要在这里,因为你只是初始化一个对象。

Is this function made obsolete by one of C++ initialization syntaxes for a subset of types? Or will it be with the upcoming of future C++ versions?

值或大括号初始化确实使该函数成为 "obsolete"。 T()T{} 会给你一个初始化值 T ,如果 T 没有默认构造函数,它将被零初始化。这意味着您可以将函数重写为

template<class T>
T zero_initialize()
{
    static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
    return {};
}

保证您的 zero_initialize 实际上零初始化对象的最普遍的可定义特征是

template <typename T>
struct can_zero_initialize :
    std::bool_constant<std::is_integral_v<
        std::remove_cv_t<std::remove_all_extents_t<T>>>> {};

不太有用。但是标准中基本类型的按位或按字节表示的唯一保证是 [basic.fundamental]/7 "The representations of integral types shall define values by use of a pure binary numeration system." 不能保证所有字节为零的浮点值是零值。不能保证所有字节为零的任何指针或成员指针值都是空指针值。 (尽管这两者在实践中通常都是正确的。)

如果平凡可复制的 class 类型的所有非静态成员都是(数组)(cv 限定的)整数类型,我认为那也可以,但是没有可能的方法来测试为此,除非反思涉及 C++。

从技术上讲,C++ 中没有对象 属性 指定用户代码可以合法地 memset C++ 对象。这包括 POD,所以如果你想成为技术人员,你的代码永远不会正确。甚至 TriviallyCopyable 也是一个 属性 关于在 现有 对象之间进行字节复制(有时通过中间字节缓冲区);它没有提到发明数据并将其推入对象的位。

话虽这么说,你可以合理地确定如果你测试is_trivially_copyable is_trivially_default_constructible.最后一个很重要,因为一些 TriviallyCopyable 类型仍然希望能够控制它们的内容。例如,这样的类型可以有一个始终为 5 的私有 int 变量,并在其默认构造函数中初始化。只要没有可以访问该变量的代码更改它,它将始终为 5。C++ 对象模型保证了这一点。

所以您不能 memset 这样的对象,并且仍然从对象模型中获得明确定义的行为。