Reputation: 7992
If I have a type like this:
class Foo {
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&) = delete;
...
private:
...
};
And I have two vectors of that type:
std::vector<Foo> x;
std::vector<Foo> y;
And I want to copy the contents of x
to y
, is there a cross-platform way of doing so?
VC++ will do this with y.assign(x.begin(), x.end())
, which uses Foo
's copy constructor instead of the deleted copy assignment operator. But GCC complains about the missing copy assignment operator, whether you try y = x
or y.assign(x.begin(), x.end())
.
Is there some way that will work in both?
Upvotes: 3
Views: 1167
Reputation: 5352
The standard requires that for y.assign(it1, it2)
to work, the element type T
be CopyAssignable. That is, copy constructible and assignable. Your type is not assignable, so you can't rely on assign
. The same goes for y = x
. Section 23.2.3 of the standard describes the requirements for various sequence container operations.
If you have an existing vector y
, you can construct a new vector z
then swap it with y
:
{
std::vector<Foo> z(x.begin(), x.end());
z.swap(y);
}
This uses the range constructor, which requires only that T
be EmplaceConstructible
, no assignment operator needed! You can then swap the underlying memory without any further copies.
Note that this may result in larger memory usage, since any existing content of y
will persist until the freshly swapped z
goes out of scope. You could try to mitigate this by first doing
y.clear();
y.shrink_to_fit();
although shrink_to_fit()
is only a request, and may not be honoured by your library implementation.
Upvotes: 4
Reputation: 141648
Prior to C++11 your code would not have been possible: the vector element type had to be CopyAssignable
, meaning copy-constructible and assignable. Note that if the type does not meet these requirements, the code is ill-formed with no diagnostic required; this gives compilers latitude to either accept or reject it.
Since C++11, individual operations have their own requirements. The relevant requirements for vector
are (source: C++14 Table 100):
emplace_back
: MoveInsertible
and MoveAssignable
insert
: CopyInsertable
and CopyAssignable
EmplaceConstructible
The meanings of these traits are (roughly):
CopyInsertable
: has copy-constructorMoveInsertable
: has copy-constructor or move-constructor (or both)CopyAssignable
: has copy-constructor and copy-assignment operatorMoveAssignable
: MoveInsertable
, and has copy-assignment or move-assignment operator (or both)EmplaceConstructible
: has constructorThe rationale here is that any insertion may cause a memory reallocation, which requires that your objects be able to be either copied or moved. However the Standard does not require the compiler to test whether constructor or assignment operator are each available, and use one if the other isn't. Instead it specifies that both must be available.
You didn't specify whether your class is movable. Assuming it isn't, then that means you cannot use any of the emplace or insert methods, nor any other function that might cause reallocation.
If you are creating y
at the time then you can of course use initialization:
vector<int> y = x;
If y
already exists, as your question seems to suggest, then you're nearly out of luck: you can't use any insertion functions, so you can only modify the existing elements. And since there is no assignment operator, you can't do this via assignment.
But you can use y.swap()
as suggested in Andrew's answer.
Consider adding move-constructor and move-assignment to your class, or redesigning your code so that you do not need to assign to y
. (E.g. use a pointer).
If x.size() == y.size()
then you can use placement new as a last-resort hack:
for (size_t i = 0; i != y.size(); ++i)
{
y[i].~Foo();
new(&y[i]) Foo(x[i]); // copy-construct
}
Upvotes: 3