Validity of pointers after moving a std::set - c++

I have a structure which conceptually has the following
class C {/* expensive class */};
struct mapping {
std::map<std::pair<C,C>, C> the_map;
};
This is less than ideal as a large number of copies of C end up being stored. My current solution is to create a set of C and then store pointers (or iterators) in the map.
struct mapping {
std::set<C> all_Cs;
std::map<std::pair<C*, C*>, C*> the_map;
};
This should be safe as the_map will always be destructed before all_Cs so all pointers will be valid.
Copy construction can be done by copies the set and reconstructing the map but is it possible to efficiently implement move construction?
From what I understand, moving a set with the same allocator is required to be a constant operation which (I assume) forces the implementation to maintain the validity of pointers to objects in the set but I cannot find anything to back this up in the standard. Using this I should be able to implement a move constructor by simply moving both the set and the map and all of the pointers will correctly be owned by the new object.
Is this this reasoning correct and can I rely on this behaviour in a portable way?

Move construction is guaranteed to preserve the objects in the container, by Table 99 (Allocator-aware container requirements). In addition to the constant complexity requirement (which precludes reallocating the elements), the post-condition for X u(rv) states:
u shall have the same elements as rv had before this construction;
Move assignment does not give the same guarantee. It will allocate new elements if the allocator's traits tell it not to propagate the allocator, and does not explicitly state that it won't otherwise.

Related

Implementation of swap function for deque with constant complexity

It is said that the std::deque swap function takes constant time,not linear.
http://www.cplusplus.com/reference/deque/deque/swap-free/. How is that function implemented then?
All resizable standard library containers (that is, all except std::array) have to store their contents in dynamically allocated memory. That is because they can grow arbitrarily large and there's no way to store arbitrarily many objects in the fixed space occupied by the container object itself. In other words, it must be possible that container.size() > sizeof(container).
This means that the container object only stores a pointer to its contents, not the contents itself. Swapping two containers therefore means simply swapping these pointers. In extremely simplified form:
template <class T>
class Container
{
T *_begin, *_end;
friend void swap(Container &a, Container &b)
{
std::swap(a._begin, b._begin);
std::swap(a._end, b._end);
}
};
Of course, in practice, this is complicated by the presence of allocators etc., but the principle is the same.
The implementation of deque is typically hidden by using pimpl idiom (each deque holds a pointer to implementation). The pointers are then swapped. It might (also) be that the deque at least holds a pointer to its buffer, which is then swapped (with related members like size).
This post (copy and swap idiom) is related to how the swap might be implemented.

Move constructors and `std::array`

According to N3485 ยง23.3.2.2:
(...) the implicit move constructor and move assignment operator for array require that T be MoveConstructible or MoveAssignable, respectively.
So, std::array supports move semantics if the type of its elements does. Great!
However, what does this really mean? I tend to picture this type as a safer version of an array providing an STL-compliant interface but, if this is true, then how can an std::array move-construct its elements? Can I do the same with an ordinary array?
However, what does this really mean?
It means that, if the element type is movable, then so is the array type.
std::array<movable, 42> move_from = {...};
std::array<movable, 42> move_to = std::move(move_from); // moves all the elements
I tend to picture this type as a safer version of an array providing an STL-compliant interface
Not really. It's a wrapper for an array, giving it the same semantics as an aggregate class - including the ability to copy and move it.
how can an std::array move-construct its elements?
In exactly the same way as any other aggregate. Its implicit move-constructor will move-construct all its members, including the elements of any member arrays.
Can I do the same with an ordinary array?
Only if you wrap it in a class type, as std::array does.
Moving a std::array is different from moving a std::vector. When moving one std::vector into another, it's (sometimes*) possible to simply re-target the internal pointers and avoid manipulating the elements at all.
With std::array, this is of course not possible - its elements have automatic storage duration, they are literally contained inside the object. However, each individual one of them can still be moved, and that's what the move operations on std::array do**.
* Assuming the allocators are compatible and don't prohibit this operation
** That's also what you get with std::vector when the buffer can't just be re-owned by the destination vector.
The default move constructor for a (non-union) class performs a member-wise move. Moving a raw array data member means moving each of the array's elements, see [class.copy]/15.
Therefore, you can move a raw array by putting it inside a class:
struct wrap
{
std::string arr[25];
};
auto w = wrap();
auto m = std::move(w); // moves the 25 `std::string`s
You can also manually invoke the move constructor of the elements, for example:
std::string a[3] = { /*...*/ };
std::string b[3] = {std::move(a[0]), std::move(a[1]), std::move(a[2])};
It is not specified if std::array contains a raw array. However, it does contain data members of the value_type, since it's guaranteed to be an aggregate. Those data members will be moved as described above when invoking the move constructor.
If the data members of a std::array are not MoveConstructible, instantiating its move constructor will fail.
You can do it using "placement new". You'll find plenty of questions on placement new already answered with many further details.
This one looks like it has a complete example:
Why is this code trying to call the copy constructor?

Move semantics with a pointer to an internal buffer

Suppose I have a class which manages a pointer to an internal buffer:
class Foo
{
public:
Foo();
...
private:
std::vector<unsigned char> m_buffer;
unsigned char* m_pointer;
};
Foo::Foo()
{
m_buffer.resize(100);
m_pointer = &m_buffer[0];
}
Now, suppose I also have correctly implemented rule-of-3 stuff including a copy constructor which copies the internal buffer, and then reassigns the pointer to the new copy of the internal buffer:
Foo::Foo(const Foo& f)
{
m_buffer = f.m_buffer;
m_pointer = &m_buffer[0];
}
If I also implement move semantics, is it safe to just copy the pointer and move the buffer?
Foo::Foo(Foo&& f) : m_buffer(std::move(f.m_buffer)), m_pointer(f.m_pointer)
{ }
In practice, I know this should work, because the std::vector move constructor is just moving the internal pointer - it's not actually reallocating anything so m_pointer still points to a valid address. However, I'm not sure if the standard guarantees this behavior. Does std::vector move semantics guarantee that no reallocation will occur, and thus all pointers/iterators to the vector are valid?
I'd do &m_buffer[0] again, simply so that you don't have to ask these questions. It's clearly not obviously intuitive, so don't do it. And, in doing so, you have nothing to lose whatsoever. Win-win.
Foo::Foo(Foo&& f)
: m_buffer(std::move(f.m_buffer))
, m_pointer(&m_buffer[0])
{}
I'm comfortable with it mostly because m_pointer is a view into the member m_buffer, rather than strictly a member in its own right.
Which does all sort of beg the question... why is it there? Can't you expose a member function to give you &m_buffer[0]?
I'll not comment the OP's code. All I'm doing is aswering this question:
Does std::vector move semantics guarantee that no reallocation will occur, and thus all pointers/iterators to the vector are valid?
Yes for the move constructor. It has constant complexity (as specified by 23.2.1/4, table 96 and note B) and for this reason the implementation has no choice other than stealing the memory from the original vector (so no memory reallocation occurs) and emptying the original vector.
No for the move assignment operator. The standard requires only linear complexity (as specified in the same paragraph and table mentioned above) because sometimes a reallocation is required. However, in some cirsunstances, it might have constant complexity (and no reallocation is performed) but it depends on the allocator. (You can read the excelent exposition on moved vectors by Howard Hinnant here.)
A better way to do this may be:
class Foo
{
std::vector<unsigned char> m_buffer;
size_t m_index;
unsigned char* get_pointer() { return &m_buffer[m_index];
};
ie rather than store a pointer to a vector element, store the index of it. That way it will be immune to copying/resizing of the vectors backing store.
The case of move construction is guaranteed to move the buffer from one container to the other, so from the point of view of the newly created object, the operation is fine.
On the other hand, you should be careful with this kind of code, as the donor object is left with a empty vector and a pointer referring to the vector in a different object. This means that after being moved from your object is in a fragile state that might cause issues if anyone accesses the interface and even more importantly if the destructor tries to use the pointer.
While in general there won't be any use of your object after being moved from (the assumption being that to be bound by an rvalue-reference it must be an rvalue), the fact is that you can move out of an lvalue by casting or by using std::move (which is basically a cast), in which case code might actually attempt to use your object.

C++11 Move semantics behaviour specific questions

I have read the below post which gives a very good insight into move semantics:
Can someone please explain move semantics to me?
but I am still fail to understand following things regarding move semantics -
Does copy elision and RVO would still work for classes without move constructors?
Even if our classes doesn't have move constructors, but STL containers has one. For operation like
std::vector<MyClass> vt = CreateMyClassVector();
and to perform operations like sorting etc. Why can't STL internally leverage move semantics to improve such operations internally using operations like copy elision or RVO which doesn't require move constructors?
3.
Do we get benefited by move semantics in below case -
std::vector< int > vt1(1000000, 5); // Create and initialize 1 million entries with value 5
std::vector< int > vt2(std::move(vt1)); // move vt1 to vt2
as integer is a primitive type, moving integer elements will not offer any advantage.
or here after move operation vt2 simply points to vt1 memory in heap and vt1 is set to null. what is actually happening? If latter is the case then even point 2 holds that we may not need move constructor for our classes.
4.
When a push_back() is called using std::move on lvalue for e.g :
std::vector<MyClass> vt;
for(int i=0; i<10; ++i)
{
vt.push_back(MyClass());
}
MyClass obj;
vt.push_back(std::move(obj));
now as vector has contiguous memory allocation, and obj is defined somewhere else in memory how would move semantics move the obj memory to vector vt contiguous memory region, wouldn't moving memory in this case is as good as copying memory, how does move justifies vectors contiguous memory requirements by simply moving a pointer pointing to a memory in different region of a heap.?
Thanks for explanation in advance!
[Originally posted as Move semantics clarification but now as the context is changed a bit posting it as new question shall delete the old one ASAP.]
Does copy elision and RVO would still work for classes without move constructors?
Yes, RVO still kicks in. Actually, the compiler is expected to pick:
RVO (if possible)
Move construction (if possible)
Copy construction (last resort)
Why can't STL internally leverage move semantics to improve such operations internally using operations like copy elision or RVO which doesn't require move constructors?
The STL containers are movable, regardless of the types stored within. However, operations on the objects in the container require the object cooperation, and as such sort (for example) may only move objects if those objects are movable.
Do we get benefited by move semantics in below case [...] as integer is a primitive type ?
Yes, you do, because containers are movable regardless of their content. As you deduced, st2 will steal the memory from st1. The state of st1 after the move is unspecified though, so I cannot guarantee its storage will have been nullified.
When a push_back() is called using std::move on lvalue [what happens] ?
The move constructor of the type of the lvalue is called, typically this involves a bitwise copy of the original into the destination, and then a nullification of the original.
In general, the cost of a move constructor is proportional to sizeof(object); for example, sizeof(std::string) is stable regardless of how many characters the std::string has, because in effect those characters are stored on the heap (at least when there is a sufficient number of them) and thus only the pointer to the heap storage is moved around (plus some metadata).
Yes.
They do, as far as possible.
Yes. std::vector has a move constructor that avoids copying all the elements.
It is still in contiguous.
e.g.
struct MyClass
{
MyClass(MyClass&& other)
: xs(other.xs), size(other.size)
{
other.xs = nullptr;
}
MyClass(const MyClass& other)
: xs(new int[other.size]), size(other.size)
{
memcpy(xs, other.xs, size);
}
~MyClass()
{
delete[] xs;
}
int* xs;
int size;
}
With a move constructor only xs and size needs to be copied into the vector (for contiguous memory), however we do not need the perform memory allocation and memcpy as in the copy constructor.

C++: How to Perform Deep Cloning of Generic Type

To keep the long story short, I am unable to use the container from the STL and boost library and have to create my own.
My own generic container is coded in VC++6 and I need to know how to manually allocate memory for generic types before storing it in my own container. The generic types are all struct that can contain nested struct. All struct be it nested or not will contain only primitive types like char*, int, bool etc.
For example, when you call the insert function of std::vector, internally, std::vector will automatically perform a deep cloning of the generic type before storing it.
How can I duplicate this functionality (deep cloning of generic type) in my own container?
Please provide some sample code for performing deep cloning of generic type.
The std::vector (and most std containers) just call the type's copy constructor. This may or may not "deep clone" the object, depending on what the copy constructor does.
First and for all: if you want to clone any object, all it's aggregates should be cloned, too. This means that every struct/class involved in the cloning action should implement cloning behavior.
Then: the stl uses so called value-semantics: containers will always contain their elements 'by value'. Copying means creating copies of all container elements.
So in order to achieve cloning/deep copy behavior, the copy constructors of every member of the container's element type should implement deep copy behavior. Members of pointer-to-object type should also be deep-copied (not just copying the member pointer).
Note: code is untested, probably contains tons of exception-unsafety etc... and is merely used as a shallow :) example.
struct WithPointer {
int* pint;
WithPointer( int value = 0 ) : pint( new int ) { *pint = value; }
WithPointer( const WithPointer& other ) {
pint = new int;
*pint = *other.pint;
}
~WithPointer( ) { delete pint; } // important!
}
This class can be 'deep-copied' using an stl container:
std::vector<WithPointer> v;
WithPointer wp(1);
v.push_back( wp );
std::vector<WithPointer> v2 = v;
std::vector does not perform any "deep cloning" by itself. When you insert something into std::vector, the vector allocates raw memory for the new element in the appropriate space and then creates a new element in that memory area by using direct initialization (normally through placement-new) from the element you passed to the insertion method. In other words, in order to make a copy for a class type, std::vector calls the copy-constructor of the class type. If the element type is not a class type or a class type without appropriately defined copy-constructor, the deep copy will not take place.
This is what you should do in your container type, if you want to simulate copying functionality of std::vector.