Several C standard library functions perform bytewise operations on objects. For instance, std::memcmp()
compares the bytes comprising the object representation of two objects, and std::memcpy()
copies the bytes comprising an object representation into a destination buffer. However, for some object types, it results in undefined or abnormal program behavior.
The C++ Standard, [class], paragraph 6 [ISO/IEC 14882-2014], states the following:
A trivially copyable class is a class that:
— has no non-trivial copy constructors,
— has no non-trivial move constructors,
— has no non-trivial copy assignment operators,
— has no non-trivial move assignment operators, and
— has a trivial destructor.
A trivial class is a class that has a default constructor, has no non-trivial default constructors, and is trivially copyable. [Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. — end note]
Additionally, the C++ Standard, [class], paragraph 7, states the following:
A standard-layout class is a class that:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions and no virtual base classes,
— has the same access control for all non-static data members,
— has no non-standard-layout base classes,
— either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
— has no base classes of the same type as the first non-static data member.
Do not use std::memset()
to initialize an object of nontrivial class type as it may not properly initialize the value representation of the object. Do not use std::memcpy()
(or related bytewise copy functions) to initialize a copy of an object of nontrivial class type, as it may not properly initialize the value representation of the copy. Do not use std::memcmp()
(or related bytewise comparison functions) to compare objects of nonstandard-layout class type, as it may not properly compare the value representations of the objects. In all cases, it is best to prefer the alternatives:.
C Standard Library Function | C++ Equivalent Functionality |
---|---|
std::memset() | Class constructor |
std::memcpy() std::memmove() std::strcpy() | Class copy constructor or operator=() |
std::memcmp() std::strcmp() | operator<() , operator>() , operator==() , or operator!=() |
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstring> #include <iostream> class C { int scalingFactor; int otherData; public: C() : scalingFactor(1) {} void set_other_data(int i); int f(int i) { return i / scalingFactor; } // ... }; void f() { C c; // ... Code that mutates c ... // Reinitialize c to its default state std::memset(&c, 0, sizeof(C)); std::cout << c.f(100) << std::endl; } |
Note that the The above noncompliant code example is compliant with EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation because all of the bits in the value representation are also used in the object representation of C
.
...
In this compliant solution, C
defines an assignment operator that is used instead of calling std::memcpy()
:.
Code Block | ||||
---|---|---|---|---|
| ||||
class C { int *i; public: C() : i(nullptr) {} ~C() { delete i; } void set(int val) { if (i) { delete i; } i = new int{val}; } C &operator=(const C &rhs) noexcept(false) { if (this != &rhs) { int *o = nullptr; if (rhs.i) { o = new int; *o = *rhs.i; } // Does not modify this unless allocation succeeds. delete i; i = o; } return *this; } // ... }; void f(C &c1) { C c2 = c1; } |
...