inserting value of a struct to pair in C++ - c++

I have read in the C++ reference for map insertion and in the example is shown the following code
std::map<char,int> mymap;
mymap.insert (it, std::pair<char,int>('b',300));
while at the same time I saw a code of a similar operation with
mymap.insert( pair<uuid,structval>( (*it).first, structval() ) );
The structval itself is declared as a normal structure
struct structval {
//some params
};
Can anyone please explain me why to initiate the pair of structval we can simply call the structure by using structval() operation? What does structval() there means? Is this a valid way to access a struct and put it as a pair?
Thank you

What does structval() there means? Is this a valid way to access a struct and put it as a pair?
structval() instantiates a temporary object of type structval. Therefore, technically, it is not a way to access a struct, but to create an instance of it. And yes, it is a valid way to create an instance of an object:
struct X
{
X() : i_(42) { }
int i_;
};
void foo(X x) { /* ... */ }
// ...
foo(X()); // <== Valid way to construct a temporary object
// of type X and pass it as an argument to
// the foo() function.
This temporary, default-constructed instance is then passed as an argument to the constructor of std::pair (and by the way, the pair object you are constructing is also a temporary!).
Usually, pairs are constructed by calling the std::make_pair() function, that can deduce the type of the arguments for you, so you don't have to type them explicitly. Therefore, you could rewrite this:
pair<uuid,structval>( (*it).first, structval() )
As the following:
std::make_pair((*it).first, structval())
In C++11 you could also use the emplace member function instead of insert():
mymap.emplace((*it).first, structval());
This would make it unnecessary to explicitly construct the pair (or to invoke std::make_pair() for that purpose).
As a final remark, the value type of a map is not:
pair<key_type, mapped_type>
But rather:
pair<key_type const, mapped_type>
// ^^^^^
Because a map's keys shall never be changed, in order to prevent messing up with the internal ordering of the container.

structval() is a call of constructor of type structval without parameters. So, simply use temporary created object.
Related: you should use std::make_pair, instead of call pair c-tor in most cases.

Related

Using try_emplace with a shared_ptr

So I have a std::unordered_map<std::string, std::shared_ptr<T>> and I'd like to use try_emplace to add/reference elements. Previously, it was simply a std::unordered_map<std::string, T> and T has a constructor which just takes a std::string, so I was using myMap.try_emplace(myString, myString).first->second to reference the T and create it if necessary.
However, if I simply change it to myMap.try_emplace(myString, std::make_shared<T>(myString)).first->second, it, of course, constructs the T every time.
What's the most idiomatic way to get around this? To clarify, I'd like to construct the object on the heap with a shared pointer and insert the shared pointer into the unordered map if and only if there is not already a matching element in the map.
You can use operator[] instead:
auto &ptr = map[ key ];
if( !ptr ) ptr = std::make_shared<T>( myString );
Note: this solution came with assumption that you do not want to keep default constructed std::shared_ptr in the map. If that is the case, then you would need to use little more verbose code with try_emplace:
auto p = map.try_emplace( key, nullptr );
if( p.second ) p.first->second = std::make_shared<T>( myString );
else {
if( !p.first->second ) { // handle nullptr }
}
auto &ptr = p.first->second;
Write a factory function wrapper:
template<class F>
struct auto_factory_t {
F f;
operator decltype(std::declval<F&&>()()) && {
return std::move(f)();
}
};
template<class F>
auto_factory_t<std::decay_t<F>> wrap_factory(F&&f){
return {std::forward<F>(f)};
}
then
myMap.try_emplace(myString, wrap_factory([&]{return std::make_shared<T>(myString);})).first->second;
and done.
wrap_factory(f) returns an object containing f. It can be implicitly cast into the type f() returns, and when done so calls f().
Another option is to wrap the shared pointer in your own type. Then you can provide a constructor that takes a T and makes a shared_ptr<T>. This lets you use try_emplace and pass to it a T without having to have anything actually created if the object already exists. There is a lot of boiler plate involved in this though if you want to use the object as if it were a shared_ptr<T>.
What you may do:
// C++17, else you have to move declaration outside of the `if`
// and retrieve it/inserted from pair result.
if (auto [it, inserted] = myMap.try_emplace(myString, nullptr); inserted) {
it->second = std::make_shared<T>(myString);
}
Modern C++17 approach
template <class factory>
class deferred_factory {
public:
deferred_factory(factory&& mfactory) : mFactory{std::move(mfactory)} {}
// Wrap invocation of #c mFactory in an appropriate conversion operator.
/* implicit */ operator std::invoke_result_t<factory>() {
return mFactory();
}
private:
factory mFactory;
};
// Deduction guide for lvalue reference factories.
template <class factory>
deferred_factory(const factory&) -> deferred_factory<const factory&>;
Explanations
The basic trick is delaying initialization, i.e. invoking the factory. This is done by initializing the value type from another type (deferred_factory) which is implicitly convertible to the desired value type.
The conversion operator returns exactly the same type as invoking the factory directly. Due to mandatory copy/move elision with C++17, this imposes no extra copies whatsoever (no further && / std::move required).
With class template argument deduction, no further helpers for creating deferred_factorys are necessary. The final deduction guide allows reusing existing factories for multiple deferred_factorys which only wrap a reference to the original factory.
Usage
In OP's context
myMap.try_emplace("key", deferred_factory{[]{return std::make_shared<T>("value");}});
inserts "key" into myMap if and only if it hasn't been present before. The shared_ptr<T> is only ever created upon insertion.
This answer is a modern variant of Yakk - Adam Nevraumont's. All credit to him.

C++ std::vector difference between creating object then adding it vs creating it inside the vector?

Since std::vector::push_back(obj) creates a copy of the object, would it be more efficient to create it within the push_back() call than beforehand?
struct foo {
int val;
std::string str;
foo(int _val, std::string _str) :
val(_val), str(_str) {}
};
int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
list.push_back(foo(val,str));
return 0;
}
// or
int main() {
std::vector<foo> list;
std::string str("hi");
int val = 2;
foo f(val,str);
list.push_back(f);
return 0;
}
list.push_back(foo(val,str));
asks for a foo object to be constructed, and then passed into the vector. So both approaches are similar in that regard.
However—with this approach a c++11 compiler will treat the foo object as a "temporary" value (rvalue) and will use the void vector::push_back(T&&) function instead of the void vector::push_back(const T&) one, and that's indeed to be faster in most situations. You could also get this behavior with a previously declared object with:
foo f(val,str);
list.push_back(std::move(f));
Also, note that (in c++11) you can do directly:
list.emplace_back(val, str);
It's actually somewhat involved. For starters, we should note that std::vector::push_back is overloaded on the two reference types:
void push_back( const T& value );
void push_back( T&& value );
The first overload is invoked when we pass an lvalue to push_back, because only an lvalue reference type can bind to an lvalue, like f in your second version. And in the same fashion, only an rvalue reference can bind to an rvalue like in your first version.
Does it make a difference? Only if your type benefits from move semantics. You didn't provide any copy or move operation, so the compiler is going to implicitly define them for you. And they are going to copy/move each member respectively. Because std::string (of which you have a member) actually does benefit from being moved if the string is very long, you might see better performance if you choose not to create a named object and instead pass an rvalue.
But if your type doesn't benefit from move semantics, you'll see no difference whatsoever. So on the whole, it's safe to say that you lose nothing, and can gain plenty by "creating the object at the call".
Having said all that, we mustn't forget that a vector supports another insertion method. You can forward the arguments for foo's constructor directly into the vector via a call to std::vector::emplace_back. That one will avoid any intermediate foo objects, even the temporary in the call to push_back, and will create the target foo directly at the storage the vector intends to provide for it. So emplace_back may often be the best choice.
You ‘d better use
emplace_back(foo(val,str))
if you are about creating and pushing new element to your vector. So you perform an in-place construction.
If you’ve already created your object and you are sure you will never use it alone for another instruction, then you can do
push_back(std::move(f))
In that case your f object is dangled and his content is owned by your vector.

Map of std::mutex: how to call emplace when constructor takes no arguments [duplicate]

I want to emplace an object into a std::map whose constructor does not take any arguments. However, std::map::emplace seems to require at least one additional argument besides the key. So how can I forward zero arguments to the constructor?
The element type of std::map<K, V> is actually std::pair<K, V>, so when you are emplacing into a map, the arguments will be forwarded to the constructor of std::pair. That's why you can't pass just the key: std::pair<K, V> can't be constructed from a single argument (unless it's another pair of the same type.) You can pass zero arguments, but then the key will be value-initialized, which is probably not what you want.
In most cases, moving values will be cheap (and keys will be small and copyable) and you should really just do something like this:
M.emplace(k, V{});
where V is the mapped type. It will be value-initialized and moved into the container. (The move might even be elided; I'm not sure.)
If you can't move, and you really need the V to be constructed in-place, you have to use the piecewise construction constructor...
M.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple());
This causes std::pair to construct the first element using k and the second element using zero arguments (value-initialization).
You could explicitly create a pair and pass that to map::emplace, or use the piecewise construction constructor of std::pair.
struct foo {};
std::map<int, foo> m;
m.emplace(std::pair<int, foo>(1, {}));
m.emplace(std::piecewise_construct,
std::forward_as_tuple(2),
std::forward_as_tuple());
Live demo
I had the same problem when I had to create an std::map of std::mutex objects. The issue is that std::mutex is neither copyable nor movable, so I needed to construct it "in place".
The accepted answer doesn't work for this case (M.emplace(k, V{}); needs V to be movable). And I didn't want to use the complicated and less readable std::piecewise_construct option (see above in other answers).
My solution is much simpler - just use the operator[] - it will create the value using its default constructor and return a reference to it. Or it will just find and return a reference to the already existing item without creating a new one.
std::map<std::string, std::mutex> map;
std::mutex& GetMutexForFile(const std::string& filename)
{
return map[filename]; // constructs it inside the map if doesn't exist
}
In C++17 you can use std::map::try_emplace, that uses std::piecewise_construct internally and doesn't look that cumbersome. It also takes a key as the first argument (instead of forwarding everything into std::pair::pair() like emplace does).
#include <map>
struct A {
A() = default;
};
int main()
{
std::map<int, A> map;
map.emplace(std::piecewise_construct,
std::forward_as_tuple(10),
std::forward_as_tuple());
// ...vs...
map.try_emplace(10);
}
Live example.
Class ToolMap()
{
friend class std::map;
public (std::map)Object ToolMap()
{
return Object;
}
}

How to emplace object with no-argument constructor into std::map?

I want to emplace an object into a std::map whose constructor does not take any arguments. However, std::map::emplace seems to require at least one additional argument besides the key. So how can I forward zero arguments to the constructor?
The element type of std::map<K, V> is actually std::pair<K, V>, so when you are emplacing into a map, the arguments will be forwarded to the constructor of std::pair. That's why you can't pass just the key: std::pair<K, V> can't be constructed from a single argument (unless it's another pair of the same type.) You can pass zero arguments, but then the key will be value-initialized, which is probably not what you want.
In most cases, moving values will be cheap (and keys will be small and copyable) and you should really just do something like this:
M.emplace(k, V{});
where V is the mapped type. It will be value-initialized and moved into the container. (The move might even be elided; I'm not sure.)
If you can't move, and you really need the V to be constructed in-place, you have to use the piecewise construction constructor...
M.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple());
This causes std::pair to construct the first element using k and the second element using zero arguments (value-initialization).
You could explicitly create a pair and pass that to map::emplace, or use the piecewise construction constructor of std::pair.
struct foo {};
std::map<int, foo> m;
m.emplace(std::pair<int, foo>(1, {}));
m.emplace(std::piecewise_construct,
std::forward_as_tuple(2),
std::forward_as_tuple());
Live demo
I had the same problem when I had to create an std::map of std::mutex objects. The issue is that std::mutex is neither copyable nor movable, so I needed to construct it "in place".
The accepted answer doesn't work for this case (M.emplace(k, V{}); needs V to be movable). And I didn't want to use the complicated and less readable std::piecewise_construct option (see above in other answers).
My solution is much simpler - just use the operator[] - it will create the value using its default constructor and return a reference to it. Or it will just find and return a reference to the already existing item without creating a new one.
std::map<std::string, std::mutex> map;
std::mutex& GetMutexForFile(const std::string& filename)
{
return map[filename]; // constructs it inside the map if doesn't exist
}
In C++17 you can use std::map::try_emplace, that uses std::piecewise_construct internally and doesn't look that cumbersome. It also takes a key as the first argument (instead of forwarding everything into std::pair::pair() like emplace does).
#include <map>
struct A {
A() = default;
};
int main()
{
std::map<int, A> map;
map.emplace(std::piecewise_construct,
std::forward_as_tuple(10),
std::forward_as_tuple());
// ...vs...
map.try_emplace(10);
}
Live example.
Class ToolMap()
{
friend class std::map;
public (std::map)Object ToolMap()
{
return Object;
}
}

Why does std::map::const_iterator call the std::pair constructor during a std::for_each, but a simple for loop does not?

I have a slightly complex data member of a class, as noted in what follows:
class BranchOutputRow
{
...
}
class Foo
{
public:
// Slightly complex data member here
std::map<boost::multiprecision::cpp_int, std::set<BranchOutputRow>> hits;
void DoLoop1()
{
// This loop calls the std::pair<> constructor
std::for_each(hits.cbegin(), hits.cend(),
[&](std::pair<boost::multiprecision::cpp_int,
std::set<BranchOutputRow>> const & hit)
{
...
}
}
void DoLoop2()
{
// This loop does NOT call the std::pair<> constructor
for (std::map<boost::multiprecision::cpp_int,
std::set<BranchOutputRow>>::const_iterator hitsPtr
= hits.cbegin();
hitsPtr != hits.cend();
++hitsPtr)
{
...
}
}
}
int main()
{
Foo foo;
foo.hits[1] = std::set<BranchOutputRow>();
foo.hits[1].insert(BranchOutputRow());
foo.DoLoop1(); // direct access to map object is not available
foo.DoLoop2(); // direct access to map object is available
}
As noted, I find that Loop #1 calls the std::pair constructor, despite the fact that the lambda function accepts its argument by reference. Therefore, in Loop 1, I do not have direct access to the object in the map, but only a copy. In my actual program, I need direct access; therefore, I must use the version indicated by Loop 2.
(In fact, Loop 2 does not call the std::pair constructor - not a surprise - and does provide direct access to the object in the map.)
I would think that std::for_each would have been carefully designed to provide the same semantics as a for loop such as Loop 2, and therefore not call the std::pair constructor, instead allowing direct access to the object in the map.
Why does Loop 1 call the std::pair constructor, despite the fact that the lambda function accepts its argument by reference?
The value_type of std::map<K,V> is std::pair<const K, V>. Your lambda takes std::pair<K,V>. Note the difference in constness.
The conversion is done through this constructor:
template< class U1, class U2 >
pair( const pair<U1, U2>& p );
(see (4) on this reference page)
The result of the conversion is a temporary, and temporaries can bind to const references, so your code works.