Copy semantics, per Plato’s Sophist

In Plato’s Sophist (translated by Nicholas P. White) we find this exchange between Theaetetus and the visiting philosopher. The visitor asks Theaetetus a question I’ve been asked many times: What do we mean by copy?

VISITOR: […] Whenever we call him a copy-maker, he’ll ask us what in the world we mean by a “copy.” We need to think, Theaetetus, about how to answer the young man’s question.

THEAETETUS: Obviously we’ll say we mean copies in water and mirrors, and also copies that are drawn and stamped and everything else like that.

VISITOR: […] He’ll ask about what runs through all those things which you call many, but which you thought you should call by the one name, “copy,” to cover them all, as if they were all one thing. Say something, then, and defend yourself, and don’t give any ground to him.

THEAETETUS: What in the world would we say a copy is, sir, except something that’s made similar to a true thing, and is another thing that’s like it?

(At this point, the dialogue goes a little off the C++ rails; the visitor seems to want to refer by the word copy to what a C++ programmer would call a reference: “not really what is, but what we call a likeness.” In C++ terms, a copy is an imitation that is indeed a second true thing, just as good as the original; and so Theaetetus could have saved himself a lot of time.)

The example I always use to explain C++’s copy semantics is:

int a = 1;
int b = a;       // b is a copy of a
b += 1;
assert(b == 2);
assert(a == 1);  // the original is unchanged

std::string a = "hello";
std::string b = a;     // b is a copy of a
b += "!";
assert(b == "hello!");
assert(a == "hello");  // the original is unchanged

Copying an object always makes a copy:

  • something with the same value as the original (“Copies are equal”),
  • but which is a second object, that you can mutate independently of the first (“Copies are disjoint”).

Copying an int copies its (integer) value. Copying a string copies its (textual) value. Copying an int* copies its (pointer) value.

int array[10];
int *a = &array[1];
int *b = a;   // b is a copy of a
b += 1;
assert(b == &array[2]);
assert(a == &array[1]);  // the original is unchanged

Copying a shared_ptr<Widget> also copies its (pointer) value: the copy compares equal to the original; that is, they both hold the same pointer value (they both “point to the same place”).

Copying a string_view copies its (string-view) value. This is a little tricky: copying a string-view value doesn’t mean the same thing as copying a string value! But remember Theaetetus: copying a sculpture doesn’t mean the same thing as copying a photograph of that sculpture. One of those copies gives you a second sculpture; the other gives you a second photograph of the same sculpture. It all depends on what we’re copying. Copy a sculpture, get a sculpture. Copy a photograph, get a photograph. Copy a string, get a string. Copy a string_view, get a string_view.

std::string sculpture = "hello";
std::string_view a = std::string_view(sculpture);
std::string_view b = a;   // b is a copy of a
assert(b == "lo");
assert(a == "hello");  // the original is unchanged

Each time you author a C++ type, you get to decide what kind of “value” is represented by that type: what it means for two such “values” to compare “equal,” what it means for them to be “disjoint,” and thus what it must mean to “copy” such a value.

Posted 2022-12-15