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

后端 未结 3 442
情话喂你
情话喂你 2020-12-09 16:22

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

template
T zero_initialize()
{
    T result;
    std::memset(&result,         


        
相关标签:
3条回答
  • 2020-12-09 16:48

    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 {};
    }
    
    0 讨论(0)
  • 2020-12-09 16:56

    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.

    0 讨论(0)
  • 2020-12-09 17:02

    The most general definable trait that guarantees your zero_initialize will actually zero-initialize objects is

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

    Not too useful. But the only guarantee about bitwise or bytewise representations of fundamental types in the Standard is [basic.fundamental]/7 "The representations of integral types shall define values by use of a pure binary numeration system." There is no guarantee that a floating-point value with all bytes zero is a zero value. There is no guarantee that any pointer or pointer-to-member value with all bytes zero is a null pointer value. (Though both of these are usually true in practice.)

    If all non-static members of a trivially-copyable class type are (arrays of) (cv-qualified) integral types, I think that would also be okay, but there's no possible way to test for that, unless reflection comes to C++.

    0 讨论(0)
提交回复
热议问题