哪些特征/概念可以保证对象的良好定义?

时间:2018-11-16 13:56:28

标签: c++ c++14 metaprogramming sfinae c++20

假设我已经定义了一个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]

3 个答案:

答案 0 :(得分:23)

从技术上讲,C ++中没有对象属性,该属性指定用户代码可以合法地memset C ++对象。其中包括POD,因此,如果您想成为技术专家,则代码永远是不正确的。甚至TriviallyCopyable都是关于在现有的对象之间进行字节复制的属性(有时通过中间字节缓冲区);它没有说明发明数据并将其推入对象的位。

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

因此,您无法memset这样的对象,仍然无法从对象模型中获得明确定义的行为。

答案 1 :(得分:8)

  

什么(最小)特征/概念可以保证对对象的精确定义?

对于std::memset reference on cppreferencememset在非 TriviallyCopyable 类型上的行为是不确定的。因此,如果可以memset一个 TriviallyCopyable ,则可以向您的班级添加一个static_assert来进行类似的检查

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来确保该类不仅是可微复制的,而且还具有一个微不足道的默认构造函数,因此我们知道将其初始化为零是安全的。

  

我应该使用std::uninitialized_fill而不是std::memset吗?为什么呢?

您不需要在这里,因为您只在初始化单个对象。

  

此函数是否由于一种C ++初始化语法中的一部分类型而过时?还是将来的C ++版本即将出现?

值或括号初始化确实会使此功能“过时”。 T()T{}将为您提供初始化的值T,如果T没有默认的构造函数,它将被初始化为零。这意味着您可以将函数重写为

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

答案 2 :(得分:0)

保证您的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“整数类型的表示应通过使用纯二进制计算系统来定义值”。不能保证所有字节均为零的浮点值是零值。不能保证所有字节为零的任何指针或指针到成员的值都是空指针值。 (尽管实际上这两种情况通常都是正确的。)

如果一个平凡可复制的类类型的所有非静态成员都是(cv限定)整数类型的(数组),我认为也可以,但是除非有反射,否则无法进行测试到C ++。