Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Let's say I have defined a zero_initialize() function:

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

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

Calling zero_initialize() for some types would lead to undefined behavior1, 2. I'm currently enforcing T to verify std::is_pod. With that trait being deprecated in C++20 and the coming of concepts, I'm curious how zero_initialize() should evolve.

  1. What (minimal) trait / concept can guarantee memsetting an object is well defined?
  2. Should I use std::uninitialized_fill instead of std::memset? And why?
  3. 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?

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

like image 482
YSC Avatar asked Nov 16 '18 13:11

YSC


2 Answers

There is technically no object property in C++ which specifies that user code can legally memset a C++ object. And that includes POD, so if you want to be technical, your code was never correct. Even TriviallyCopyable is a property about doing byte-wise copies between existing objects (sometimes through an intermediary byte buffer); it says nothing about inventing data and shoving it into the object's bits.

That being said, you can be reasonably sure this will work if you test is_trivially_copyable and is_trivially_default_constructible. That last one is important, because some TriviallyCopyable types still want to be able to control their contents. For example, such a type could have a private int variable that is always 5, initialized in its default constructor. So long as no code with access to the variable changes it, it will always be 5. The C++ object model guarantees this.

So you can't memset such an object and still get well-defined behavior from the object model.

like image 187
Nicol Bolas Avatar answered Oct 24 '22 03:10

Nicol Bolas


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

Per the std::memset reference on cppreference the behavior of memset on a non TriviallyCopyable type is undefined. So if it is okay to memset a TriviallyCopyable then you can add a static_assert to your class to check for that like

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;
}

Here we use std::is_trivial_v to make sure that not only is the class trivially copyable but it also has a trivial default constructor so we know it is safe to be zero initialized.

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

You don't need to here since you are only initializing a single object.

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?

Value or braced initialization does make this function "obsolete". T() and T{} will give you a value initialized T and if T doesn't have a default constructor it will be zero initialized. That means you could rewrite the function as

template<class T>
T zero_initialize()
{
    static_assert(std::is_trivial_v<T>, "Error: T must be TriviallyCopyable");
    return {};
}
like image 10
NathanOliver Avatar answered Oct 24 '22 03:10

NathanOliver