Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this code from "The C++ Programming Language 4th Edition" Section 19.3.3.1 valid?

Tags:

c++

Section 19.3 presents a string representation in a chapter whose main focus is operator overloading, specifically the special operators [] , ->, and (). It implements copy_from() as an ancillary function as follows:

void String::copy_from(const String &x)
    // make *this a copy of x
{
    if (x.sz <= short_max)
    {
        memcpy(this, &x, sizeof(x);
        ptr = ch;
    }
    else
    {
        ptr = expand(x.ptr, x.sz+1);
        sz = x.sz;
        space = 0;
    }
}

The class interface looks like this:

#ifndef STRING_EXERCISE_H
#define STRING_EXERCISE_H

namespace simple_string
{
    class String;
    char *expand(const char *ptr, int n);
}

class String
{
    public:
        String(); // default constructor x{""}
        explicit String(const char *p); // constructor from C-style string

        String(const String &s); // copy constructor
        String &operator=(const String& s); // copy assignment
        String(String &&s) // move constructor
        String &operator=(String &&s) // move assignement

        ~String() // destructor

        char &operator[](int n); // unchecked element access
        char operator[](int n) const;
        char &at(int n); // checked element access
        char at(int n) const;

        String &operator+=(char c) // add char c to the end

        const char *c_str(); // c-style string access
        const char *c_str() const;

        int size() const; // number of elements
        int capacity() const; // elements plus available space

    private:
        static const short short_max = 15;
        int sz;
        char *ptr;
        union
        {
            int space; // unused allocated space
            char ch[short_max+1]; // leave space for terminating 0
        };

        void check(int n) const; // range check
        void copy_from(const String &x);
        void move_from(String &x);
}

#endif

How can String::copy_from() use memcpy() to copy the class? I thought the class copied had to be trivially copyable (which it is not, because String has user-defined constructors, copy operations, move operations, and destructors).

like image 384
dav Avatar asked Jul 17 '18 16:07

dav


1 Answers

How can String::copy_from() use memcpy() to copy the class?

int, char, and the anonymous union are all trivially copyable. So while you cannot perform a memcpy of a String, you can perform a memcpy of its members. All of them, all at once. The technically correct code for this would be:

memcpy(&this->sz, &x.sz, sizeof(x));

That copies the range of memory of the storage for the members of this object.That is guaranteed by the rules of standard layout types. For standard layout types, the members are stored in definition order. So if you start with the first, and cover the range of all objects, that should copy the members.

However, the standard also says that the first member subobject of a standard layout type is required to have the same address as the object itself:

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member.

That means &this->sz must be the same address as this, and &x.sz must be the same address as &x.

So just cut out the middle-man:

memcpy(this, &x, sizeof(x));

This is only allowed because of the rules of standard layout types.


A bigger issue is that copy_from never checks for self assignment. memcpy doesn't work with overlapping memory ranges. Maybe operator= and similar functions check for this already.

like image 56
Nicol Bolas Avatar answered Sep 24 '22 15:09

Nicol Bolas