1
votes

I know a POD class can safely be memset. In c++17(?) std::is_pod is being replaced by std::is_standard_layout and std::is_trivial. Can a non trivial but standard layout struct be safely memset IF it only contains char[] data and a user defined constructor.

Legacy class that has dozens of char[] member variables.

#include <cstring>
#include <memory>
#include <iostream>
#include <type_traits>

struct A
{
    // dozens of fields all char[] which need to initialized to '0'
    char foo[10]; // example char[] field
    // dozens of other fields all char[] which need to be initialized to non '0'

    struct AA
    {
        char bar[10];
        // dozens of other fields
    } sub[7];
};

// What I want to turn struct A into
struct B
{
    B()
    {
        // My question: Is this permissible?
        std::memset(this, '0', sizeof(*this));

        // Initialize all the other member variables that need different values
        std::memset(foo, ' ', sizeof(foo));
    }

    // dozens of fields all char[] which need to initialized to '0'
    char foo[10]; // example char[] field
    // dozens of other fields all char[] which need to be initialized to other values in constructor

    struct BB
    {
        char bar[10];
        // dozens of other fields
    } sub[7];
};

int main(int argc, char* argv[])
{
    bool isTrivialA = std::is_trivial<A>::value;
    bool isStandardLayoutA = std::is_standard_layout<A>::value;
    bool isTrivialCopyA = std::is_trivially_copyable<A>::value;

    std::cout << "Is A trivial: " << isTrivialA << std::endl;
    std::cout << "Is A standard layout: " << isStandardLayoutA << std::endl;;
    std::cout << "Is A trivial copy: " << isTrivialCopyA << std::endl << std::endl;

    bool isTrivialB = std::is_trivial<B>::value;
    bool isStandardLayoutB = std::is_standard_layout<B>::value;
    bool isTrivialCopyB = std::is_trivially_copyable<B>::value;

    std::cout << "Is B trivial: " << isTrivialB << std::endl;
    std::cout << "Is B standard layout: " << isStandardLayoutB << std::endl;
    std::cout << "Is B trivial copy: " << isTrivialCopyB << std::endl;
}

Existing code just does a memset on the object when it's used.

A legacy;
memset(&legacy, '0', sizeof(legacy));

I would like call the user defined constructor on the updated struct B because there are dozens of fields which actually need to be initialized to different values besides '0'. I know I could have a non member function to initialize the values but that seems clunky.

B newer; // Call user defined constructor

Is the constructor defined in B with memset() of the 'this' pointer safe / allowed?

1
Not sure about memset, but you can easily add {} initializer after every field to zero them by default. (It's not even necessary for fields in nested structs.)HolyBlackCat
What HolyBlackCat said worked for me, char foo[10] = {}; and sub[7] = {}; in the struct declaration, and now everything interesting is 0 initialized. (The "not interesting" parts would be if there was any padding, which in the struct A example there isn't on my platform.)Eljay

1 Answers

4
votes

There is no mechanism which permits "memset" to be a valid operation on an object. And this includes "POD"; there was never anything in the standard that said it was OK to just arbitrarily write data directly into the object representation of POD types.

Trivially copyable makes it OK to copy the object representation into storage, and it makes it OK to copy such data that was taken from a valid instance of the type directly into another instance of that type. But even that designation never made it OK to arbitrarily set values directly into an object's representation.

Really though, it'd be easier to just use () or {} to value initialize the object, which (if it has no contructors, thus fitting the old POD definition) will value initialize the members.