What happens if to a movable object if I call
std::set<>::insert
on it, and the insertion doesn't take place
because there is already an object with the same key present in
the set. In particular, is the following valid:
struct Object
{
std::string key;
// ...
struct OrderByKey
{
bool operator()( Object const& lhs, Object const& rhs) const
{
return lhs.key < rhs.key;
}
};
Object( Object&& other )
: key(std::move(other.key))
// ...
{
}
};
and:
std::set<Object, Object::OrderByKey> registry;
void
register(Object&& o)
{
auto status = registry.insert(std::move(o));
if (!status.second)
throw std::runtime_error("Duplicate entry for " + o.key);
}
After the registry.insert
, when no insertion takes place, has
o
been moved or not. (If it might have been moved, I need to
save a copy of the string before hand, in order to use it in the
error message. (And yes, I know that I can always write:
throw std::runtime_error( "Duplicate entry for " + status->first.key );
Which is what I'll probably do. But I would still like to know what the standard says about this.)
A std::move()
ed object passed to a standard library function should be considered to be moved from: the library is free to consider the object to be its only reference, i.e., it may move from it even if it doesn't use it. The relevant clause is 17.6.4.9 [res.on.arguments] paragraph 2, third bullet (it seems identical in C++11 and C++14):
If a function argument binds to an rvalue reference, the implementation may assume that this parameter is a unique reference to this argument. ...
This is (very simmilar to) LWG Active issue 2362 which deals with emplace. IIRC the sentiment is that it should be guaranteed that the object is only moved iff it is inserted/emplaced. With emplace it seems to be not trivial to achieve. I do not remember if the situation is easier for insert.
The third bullet of [res.on.arguments]/1 from N3936:
If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [ Note: If the parameter is a generic parameter of the form
T&&
and an lvalue of typeA
is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. —end note ] [ Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argumentmove(x)
), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. —end note ]
despite on its face being about aliasing, can be interpreted as allowing the implementation to do virtually anything to an argument that is passed to a standard library function bound to an rvalue reference. As Fabio says, there are situations in which tightening this specification could be useful, and the committee is looking at some of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With