Suppose I have a weird string type, that either owns or doesn't own it's underlying buffer:
class WeirdString {
private:
char* buffer;
size_t length;
size_t capacity;
bool owns;
public:
// Non-owning constructor
WeirdString(char* buffer, size_t length, size_t capacity)
: buffer(buffer), length(length), capacity(capacity), owns(false)
{ }
// Make an owning copy
WeirdString(WeirdString const& rhs)
: buffer(new char[rhs.capacity])
, length(rhs.length)
, capacity(rhs.capacity)
, owns(true)
{
memcpy(buffer, rhs.buffer, length);
}
~WeirdString() {
if (owns) delete [] buffer;
}
};
Does that copy constructor violate the standard somewhere? Consider:
WeirdString get(); // this returns non-owning string
const auto s = WeirdString(get());
s
is either owning or non-owning depending on whether or not the additional copy constructor got elided, which in C++14 and earlier is permitted but optional (though in C++17 is guaranteed). That Schrödinger's ownership model suggests that this copy constructor is, in itself, undefined behavior.
Is it?
A more illustrative example might be:
struct X {
int i;
X(int i)
: i(i)
{ }
X(X const& rhs)
: i(rhs.i + 1)
{ } ~~~~
};
X getX();
const auto x = X(getX());
Depending on which copies get elided, x.i
could be 0, 1, or 2 more than whatever was returned in getX()
. Does the standard say anything about this?
f() + g()
, it is unspecified whetherf
org
get called first; this is not, by itself, a reason to declare that the expression exhibits undefined behavior. It's possible, of course, thatg
somehow relies on a side effect produced byf
, and exhibits undefined behavior in its absence. Yours is a similar situation: copy constructor may or may not be elided, and you may end up with owning or non-owning instance - that by itself does not trigger undefined behavior; but it's possible that something further down relies on the instance being in a particular state, and gets disappointed. – Aeniahvector<X>
would be undefined behavior becauseX
is notCopyConstructible
. (assumeX
had a move ctor that similarly did something odd) – MunichCopyConstructible
only means that the class provides a copy constructor. It doesn't mandate any particular behavior of said constructor. Both classes you show satisfyCopyConstructible
requirement, and you can happily have a vector thereof. I'm not sure where you see a source of undefined behavior. – AeniahWeirdString
orX
are or are not "equivalent" for the purposes of these requirements. I would argue it's a defect in the standard. – AeniahMyClass a; MyClass b = a; if (a != b) cout << "HELP";
– Soniasonic