Do I understand correctly, that since std::map's node-handle insertion takes r-value:
insert_return_type std::map::insert(node_type&& nh),
the node-handle cannot be used (to change value) after insertion?
std::map<...> m;
// Wrong:
m.insert(nh);
nh.value() = new_value;
// Right:
auto it = m.insert(nh);
it->second = new_value;
The object at nh will be copied out of, but the returned iterator will point to the new copy that exists inside the map.
The first example is indeed wrong, since nh can't be used for anything other than destruction after being moved from.
That's generally true, even without the r-value form: the map creates a value that lives inside its own data structure, and is a copy of the one being passed in to insert. For the regular (const &) form, you understand that changing the original will not affect the map!
Related
Using std::set in C++, the only way I can find to remove an item from a set is to use the erase method. This deletes the item in question, which I don't want to happen. The only way I can think of to remove an item from a set without deleting it would be to create a new set and add all the items of the old set to it iteratively, making sure to not add the item that needs to be removed from the set, then deleting the old set.
Is there a cleaner way to do this?
You can't remove an item from a set without deleting it. Sets own their members. If the member is removed from the set, it doesn't exist anymore. If you want to be able to remove something without deleting it, don't add it to a set.
Imagine if you have int x[5]; x[2]=2;. How can you get x[2] out of the array? What would that even mean? You can, of course, construct a new integer with the same value, int j = x[2];. But that's a new object (with the same value) that is not extending the life of the existing object.
Depending on what your outer problem is, there might be a solution. For example, you could add a std::unique_ptr to an object into a set and then you can destroy that std::unique_ptr without destroying the object it points to be constructing a new std::unique_ptr to the same underlying object.
Moving an object out of a set
You can use extract to remove the corresponding node from the set. This gives you a handle to the node. Once you have the handle, you can move the item out of the handle.
template<class T>
T find_and_remove(std::set<T>& s, T const& elem) {
auto iterator = s.find(elem);
if(iterator == s.end() {
throw std::invalid_argument("elem not in set");
}
// Remove element, return node handle
auto node_handle = s.extract(iterator);
return std::move(node_handle.value());
}
Alternatively, if you already have the iterator to the node, you can write it like this:
template<class T>
T remove_from_set(std::set<T>& s, std::set<T>::iterator it) {
return std::move(s.extract(it).value());
}
Moving the value transfers ownership of any resources owned by the value. For example, if the set contains a string, the contents of the string won't be deleted, and any iterators to the string won't be invalidated.
The caveat of this is that if you had pointers or references to the object from when it was still in the set, these will be invalidated.
Extracting the object itself without a move and without invalidating any pointers or references to the object
This is the less common case, but if you had a reference or pointer to the object in the set, you may want to do this.
Again, we can use the extract function:
auto node_handle = s.extract(my_object);
Or:
auto node_handle = s.extract(my_iterator);
You can access the stored object with node_handle.value(), which returns a reference to the object. The object won't be deleted until the node_handle is deleted, and if you need to extend it's lifetime further, you can return the node_handle from a function without the object being deleted.
The best way to explain what I'm trying is accomplish is with this example (compiled with Visual Studio 2008 SP1):
struct ELEMENT1{
//Its members
ELEMENT1()
{
//Constructor code
}
~ELEMENT1()
{
//Destructor code
}
};
std::map<std::wstring, ELEMENT1> map;
std::pair<std::map<std::wstring, ELEMENT1>::iterator, bool> resIns;
ELEMENT1 element;
std::wstring strKey;
for(size_t i = 0; i < numberRepetitions; i++)
{
//Do processing
//...
//set 'strKey'
//Insert new element into the map first
resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element)); //This line calls ELEMENT1 constructor & destructor twice
//Then fill out the data
fill_in_data(resIns.first->second);
}
BOOL fill_in_data(ELEMENT1& outInfo)
{
//Fill in 'outInfo' -- MUST be in its own function
//...
}
My goal is to optimize this code, and thus I did the following:
Moved ELEMENT1 element construction/destruction outside of the loop.
I'm inserting the element into the map and then attempt to fill it out using the pointer to the inserted element instead of constructing new element, then filling it out, then copying it into the map, and then destroying it. (At least that was the plan.)
But when I compile this for a Release build and check the assembler code, I can see that the C++ line with map.insert() function calls ELEMENT1 constructor twice! and then twice its destructor. So the following machine code is just for that map.insert() line:
So I'm obviously not seeing something here.
Can someone suggest what's going on in that compiled code & if it's possible to optimize it?
The reason you have 2 constructor calls is because what you are passing to insert does not match what it need. std::map::insert takes a const value_type& and value_type for a map is
std::pair<const key_type, element_type>
^^^^^ this is important
So, since they do not match you construct one element when you use
std::pair<std::wstring, ELEMENT1>(strKey, element)
and then the compiler calls the copy constructor to convert that into a
std::pair<const std::wstring, ELEMENT1>
A quick fix is to change the code to
std::pair<const std::wstring, ELEMENT1>(strKey, element)
Which leaves you with one temporary that is constructed and destructed. You can also do as zett42 suggests in their answer to avoid the creation of the temporary entirely.
resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element));
You are constructing a temporary std::pair whose member second is a ELEMENT1. This causes the copy constructor of ELEMENT1 to be called.
The 2nd call to the copy constructor of ELEMENT1 is when std::map::insert() creates a new element in the map that will be initialized by the temporary std::pair.
You can avoid the duplicate constructor call caused by the temporary by using std::map::operator[] instead:
ELEMENT1& resIns = map[ strKey ];
fill_in_data( resIns );
If strKey doesn't already exist in the map, an ELEMENT1 will be default-constructed directly within the map and a reference to the new object will be returned. The constructor will be called exactly one time.
If strKey already exists in the map, a reference to the existing object will be returned.
You should use emplace to avoid creation on temp objects:
resIns = map.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(strKey)
, ::std::forward_as_tuple()
);
A good reason to switch to newer VS version.
I'm designing a class for my application that implements a lot of standard shared pointers and usage of standard containers such as std::map and std::vector
It's very specific question to the problem so I just copied a piece of code
from my header for clarification purposes..
here is a snapshot of that declarations from the header:
struct Drag;
std::map<short, std::shared_ptr<Drag>> m_drag;
typedef sigc::signal<void, Drag&> signal_bet;
inline signal_bet signal_right_top();
and here is one of the functions that uses the above declarations and a temporary shared_ptr which is intended to be used not only in this function but until some late time. that means after the function returns a shared pointer should be still alive because it will be assigned at some point to another shared_ptr.
void Table::Field::on_signal_left_top(Drag& drag)
{
m_drag.insert(std::make_pair(drag.id, std::make_shared<Drag>(this))); // THIS!
auto iter = m_drag.find(drag.id);
*iter->second = drag;
iter->second->cx = 0 - iter->second->tx;
iter->second->cy = 0 - iter->second->ty;
invalidate_window();
}
the above function first insert a new shared_ptr and then assigns the values from one object into another,
What I need from your answer is to tell whether is it safe to insert temporary shared_ptr into the map and be sure that it will not be a dangling or what ever bad thing.
According to THIS website the above function is not considered safe because it would much better to write it like so:
void Table::Field::on_signal_left_top(Drag& drag)
{
std::shared_ptr pointer = std::make_shared<Drag>(this);
m_drag.insert(std::make_pair(drag.id, pointer));
auto iter = m_drag.find(drag.id);
*iter->second = drag;
// etc...
}
well one line more in the function.
is it really required to type it like that and why ?
There's no difference between the two functions in regard to the std::shared_ptr, because the std::make_pair function will create a copy of the temporary object before the temporary object is destructed. That copy will in turn be copied into the std::map, and will then itself be destructed, leaving you with a copy-of-a-copy in the map. But because the two other objects have been destructed, the reference count of the object in the map will still be one.
As for handling the return value from insert, it's very simple:
auto result = m_drag.insert(...);
if (!result.second)
{
std::cerr << "Could not insert value\n";
return;
}
auto iter = result.first;
...
The code in the example given is different from your example code, because it is using the new operator instead of std::make_shared. The key part of their advice is here:
Since function arguments are evaluated in unspecified order, it is possible for new int(2) to be evaluated first, g() second, and we may never get to the shared_ptr constructor if g throws an exception.
std::make_shared eliminates this problem - any dynamic memory allocated while constructing an object within std::make_shared will be de-allocated if anything throws. You won't need to worry about temporary std::shared_ptrs in this case.
I'm playing around Move Semantics and [r|l]value references to learn how to use them in real-world programs. Consider following code:
// Item is a heavy class having move ctor and assignment but no copy.
std::map<std::string, Item*> lookup;
std::forward_list<Item> items;
void FooClass::addItem(Item&& d) {
if (lookup.find(d.getName()) == lookup.end()) {
lookup[d.getName()] = &d; //<== not safe after move?
items.push_front(std::move(d));
}
}
I'm getting address of an Item&& and store it in a pointer. Then move data that to a std::forward_list (items). I assume calling move assignment does not affect address of object. Is that correct? Though content of d is no more valid after move. That is content of lookup table (lookup) is incorrect.
I'm assuming that I have to re-order a) adding lookup item and b) move actual data. Above code is not sane. Is this correct?
Also I can't see why do I have to say std::move there. Compiler should know that d is a rvalue reference. So it should call std::forward_list<T>::push_front(T&&) and move assignment...
lookup.[d.getName()] = &d; //<== not safe after move?
This is completely unsafe, but not just because of the move. Contrary to your question's title, you are not taking the address of an rvalue reference, you are taking the address of an lvalue, but one which is probably going to go out of scope soon after the function returns, which will leave a dangling pointer. Consider:
FooClass f;
f.addItem( Item() );
This adds the address of a temporary to the map, so if you ever dereference the pointer your program has undefined behaviour, the epitome of unsafe.
The move on the next line might make things worse, because the object referred to by the pointer in the map might get modified by the move, leaving a pointer to a moved-from Item in the map, but that's nothing compared to the undefined behaviour that results from it going out of scope after the function returns.
It's trivial to make the code safe, so there is no reason to write it the way you have done.
items.push_front(std::move(d));
auto& item = items.front();
lookup[item.getName()] = &item;
Now the pointer in the map refers to an object which is not about to go out of scope. The pointer will be valid as long as the element is in the forward_list.
I would get rid of items and change lookup to store Item by value, as in:
using Lookup = std::map<std::string, Item>;
Lookup lookup;
void addItem(Item&& d)
{ lookup.insert(std::pair<std::string const&, Item&&>{d.getName(), std::move(d)}); }
class T
{
unordered_map<string, int> table;
...
void updateA(const unordered_map<string, int>::iterator& iter)
{
iter->second = 100;
}
void updateB(unordered_map<string, int>::iterator iter)
{
iter->second = 100;
}
};
Question> Which function is better(i.e. updateA or updateB)? If you have a better one, please propose.
Thank you
1) First, to answer the question in the title, is it necessary to pass iterators by (const) reference: No. An iterator acts as a proxy for the data item in the container, regardless of whether or not the iterator itself is a copy of or a reference to another iterator. Also in the case when the iterator gets invalidated by some operation performed on the container, whether you maintain it by copy or reference will not make a difference.
2) Second, which of the two options is better.
I'd pass the iterator by copy (i.e. your second option).
The rule of thumb is: Pass by reference if you either want to modify the original variable passed to the function, or if the object you pass is large and copying it involves a major effort.
Neither is the case here: Iterators are small, lightweight objects, and given that you suggested a const-reference, it is also clear that you don't want to make a modification to it that you want reflected in the variable passed to the function.
3) As a third option, I'd like you to consider adding const to your second option:
void updateC(const unordered_map<string,int>::iterator iter)
{
iter->second = 100;
}
This ensures you won't accidentally re-assign iter inside the function, but still allows you to modify the original container item referred to by iter. It also may give the compiler the opportunity for certain optimizations (although in a simple situation like the one in your question, these optimizations might be applied anway).