How to make map.emplace automatically choose constructor in c++17? - c++

In c++17 first, I have a struct like
typedef struct ST_A{
int a;
string s;
ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
And Now I have a simple map and emplace twice in terms of
cppreference
map<string, st_A> m1;
m1.emplace("c", 10, "c"); ---> case 1
m1.emplace(std::piecewise_construct, ---> case 2
std::forward_as_tuple("c"),
std::forward_as_tuple(10, "c"));
Case 2 works fine but case 1 compiles in error. So how to solve it in case 1 and make emplace as concise as possible?
What if the map is much more complex? Like
map<string, deque<pair<int, st_A>>> m2, how to emplace it as concise as possible?
What if self-define struct has more constructors?Like
typedef struct ST_A{
int a = 0;
string s;
ST_A(const int a) :a(a) {}
ST_A(const string&s) :s(s) {}
ST_A(const int a, const string&s) :a(a),s(s) {}
}st_A;
would it confuse the compiler if there actually is some consice way to emplace as the above link in cppreference.
UPDATE
If my original map is map<string, deque<pair<int, st_A>>> m2, and now I want to add an element of pair{"c1", pair{1, st_A{10, "c"}}}.That is to say, after adding, m2 changes into map{{"c1",deque{pair{1,st_A{10, "c"}}}}
with the form of insert codes is like
m2.insert({"c1", {{1, {10, "c"}}}});
but it may call too many ctor/copy ctor, if I want to write in a more efficient way in performance, how could it be?

emplace() constructs std::pair<const Key,Value> from the passed arguments.
Without std::piecewise_construct, you have to pass exactly two arguments, one to construct the key, one to construct the value:
m1.emplace("c", st_A{10, "c"});
Which defeats the purpose of "emplacing" since you are constructing the value ahead of the call.
My recommendation is to use try_emplace which has a more friendly API, especially if the key is simple and allows exactly what you want:
auto [it, inserted ] = m1.try_emplace(Key{key_args...}, value_args...);
auto [it, inserted ] = m1.try_emplace("c", 10, "c");
In general, the first argument constructs the key, the rest construct the value. Key ctor can thus be omitted if it accepts just one argument.
The return value indicates whether the element has been inserted or if there is already an element with the same key present. it returns basically m1.at("c").
The key is always constructed, the value arguments are only "consumed"(=moved from) if inserted==true. Otherwise they are untouched.
Example
For map<string, deque<pair<int, st_A>>> m2;, you could the the following:
m2["C"].emplace_back(1, st_A{10, "c"});
std::deque does not have a constructor that accepts a single element, aggregate initialization does not work either. Not that it would save any copies, std::deque::emplace_back achieves that already. So m2["C"] does the construction efficiently already.
If you want to also elide st_A temporary, you can again fallback to std::pair's std::piecewise_construct:
m2["C"].emplace_back(std::piecewise_construct, std::forward_as_tuple(1),
std::forward_as_tuple(10, "c"));

Related

C++ using move semantics to pass argument to another function

I have function which takes 2 arguments, but one of these (map container) is passed to another function:
void myFunc(const std::map<std::string, int> &myMap, int num) {
int x = internalFunc(myMap);
// do some stuff...
}
int internalFunc(const std::map<std::string, int> &myMap) {
// return some map statistics
}
somewhere in main.cpp :
std::map<std::string, int> map1{ {"Hello", 10}, {"Hello2", 20}, {"Hello3", 30} };
myFunc(map1, 20);
My question is:
Is move semantics a good way for optimising this piece of code (passing one argument to another function using move) like this:
int internalFunc(std::map<std::string, int> &&myMap) {
// now gets rvalue reference
// return some map statistics
}
void myFunc(std::map<std::string, int> myMap, int num) {
int x = internalFunc(std::move(myMap));
// do some stuff...
}
I prefer not to use universal reference (using template) and std::forward in this case because this function is always called with this type of map and I prefer to keep the code as simple as possible.
internalFunc is always called by this one specific function myFunc.
Is move semantics good way for optimising this kind of functions? I understand that optimising using move semantics depends on moved object type, but let's stick to the example above with standard map container.
Thank you
Move semantics are useful if you need to modify the map or if the function needs to take ownership of the map for some reason (maybe the latter is more understandable if the functions were class members, i.e. setters or constructors for instance).
You should use const std::map<std::string, int>& for 3 main reasons:
You only want read access.
Readability: the user will understand quickly that the map won't be modified.
You won't get better results using move semantics.
Note on move semantics
If you use move semantics, the arguments of your functions don't necessarily need the double &&. Generally, it is better to omit them (except for perfect forwarding and non-copyable objects like stream objects). The && requires that the passed arguments be rvalues. However, omitting the && does not mean you cannot pass the arguments as rvalues. Let's see this with an example:
int internalFunc(std::map<std::string, int> myMap)
{
/* ... */
return 1; // or whatever
}
void myFunc(std::map<std::string, int> myMap, int num)
{
int x = internalFunc(std::move(myMap));
// do some stuff...
}
The arguments myMap in the above code don't get copied if you pass them as rvalues:
int main()
{
myFunc({ {"Hello", 10}, {"Hello2", 20}, {"Hello3", 30} }, 20);
return 0;
}
Moreover, you can now use the same code by passing lvalues, like you do in the main function of your question (myFunc(map1, 20);). The argument myMap in myFunc is then a copy of course. Also, your version with move semantics would not compile in this case.
If you really want to make sure the maps don't get copied, you can use the &&, but this is rather rare, and, in my opinion, should be used only for objects which cannot be copied (i.e. stream objects).

Construct-in-place an unmoveable object in a map

I'm trying to construct an object in a map that contains an atomic, so it can neither be copied nor moved AFAICT.
My reading of C++ reference is that map emplace should be able to do this. But the following code does not compile because of deleted or non-existent constructors. Using make_pair does not help.
#include <atomic>
#include <unordered_map>
class Z {
std::atomic<int> i;
};
std::unordered_map<int, Z> map;
void test(void) {
map.emplace(0, Z()); // error
map[0] = Z(); // error
}
Is this possible, and if not, why not?
EDIT: Compiler is gcc 4.8.1, on Linux
map.emplace(std::piecewise_construct, std::make_tuple(0), std::make_tuple()) will construct a zero-argument Z at location 0.
map[0] will also do it if it is not already there.
emplace takes the arguments to construct a std::pair<const K, V>. std::pair has a std::piecewise_construct_t tagged constructor that takes two tuples, the first is used to construct the first argument, the second to construct the second argument.
so std::pair<const int, Z> test( std::piecewise_construct, std::make_tuple(0), std::make_tuple() ) constructs the tests elements in-place, the const int is constructed with (0). The Z is constructed with ().
map.emplace forwards is arguments to the std::pair constructor.
The simplest solution is to use operator[] to construct the value inside the map. Then you can assign a value (or operate on it as needed).
May be the following solution will be better, since atomic is not copyable:
class Z {
std::atomic<int> i;
};
std::unordered_map<int, std::shared_ptr<Z>> map;
void test(void) {
map.emplace(0, std::make_shared<Z>()); // OK
}

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 is set::find not a template?

With template functions from <algorithm> you can do things like this
struct foo
{
int bar, baz;
};
struct bar_less
{
// compare foo with foo
bool operator()(const foo& lh, const foo& rh) const
{
return lh.bar < rh.bar;
}
template<typename T> // compare some T with foo
bool operator()(T lh, const foo& rh) const
{
return lh < rh.bar;
}
template<typename T> // compare foo with some T
bool operator()(const foo& lh, T rh) const
{
return lh.bar < rh;
}
};
int main()
{
foo foos[] = { {1, 2}, {2, 3}, {4, 5} };
bar_less cmp;
int bar_value = 2;
// find element {2, 3} using an int
auto it = std::lower_bound(begin(foos), end(foos), bar_value, cmp);
std::cout << it->baz;
}
In std::set methods like find you have to pass an object of type set::key_type which often forces you to create a dummy object.
set<foo> foos;
foo search_dummy = {2,3}; // don't need a full foo object;
auto it = foos.find(search_dummy);
It would be so helpful if one can call just foos.find(2). Is there any reason why find can't be a template, accepting everything that can be passed to the less predicate. And if it is just missing, why isn't it in C++11 (I think it isn't).
Edit
The main question is WHY isn't it possible and if it was posiible, WHY decided the standard not to provide it. A a second question you can propose workarounds :-) (boost::multi_index_container crosses my mind just now, which provides key extraction from value types)
Another Example with a more expensive to construct value type. The key name is part of the type and should not be used as a copy in maps key;
struct Person
{
std::string name;
std::string adress;
std::string phone, email, fax, stackoferflowNickname;
int age;
std::vector<Person*> friends;
std::vector<Relation> relations;
};
struct PersonOrder
{
// assume that the full name is an unique identifier
bool operator()(const Person& lh, const Person& rh) const
{
return lh.name < rh.name;
}
};
class PersonRepository
{
public:
const Person& FindPerson(const std::string& name) const
{
Person searchDummy; // ouch
searchDummy.name = name;
return FindPerson(searchDummy);
}
const Person& FindPerson(const Person& person) const;
private:
std::set<Person, PersonOrder> persons_;
// what i want to avoid
// std::map<std::string, Person> persons_;
// Person searchDummyForReuseButNotThreadSafe;
};
std::find_if works on an unsorted range. So you can pass any predicate you want.
std::set<T> always uses the Comparator template argument (std::less<T> by default) to maintain the order of the collection, as well as find elements again.
So if std::set::find was templated, it would have to require that you only pass a predicate that observes the comparator's total ordering.
Then again, std::lower_bound and all the other algorithms that work on sorted ranges already require exactly that, so that would not be a new or surprising requirement.
So, I guess it's just an oversight that there's no find_if() (say) on std::set. Propose it for C++17 :) (EDIT:: EASTL already has this, and they used a far better name than I did: find_as).
That said, you know that you shouldn't use std::set, do you? A sorted vector will be faster in most cases and allows you the flexibility you find lacking in std::set.
EDIT: As Nicol pointed out, there're implementations of this concept in Boost and Loki (as well as elsewhere, I'm sure), but seeing as you can't use their main advantage (the built-in find() method), you would not lose much by using a naked std::vector.
The standard states that std::set::find has logarithmic time complexity. In practice this is accomplished by implementing std::set as a binary search tree, with a strict weak ordering comparison used as sorting criteria. Any look-up that didn't satisfy the same strict weak ordering criteria wouldn't satisfy logarithmic complexity. So it is a design decision: if you want logarithmic complexity, use std::set::find. If you can trade complexity for flexibility, use std::find_if on the set.
They've provided for what you want, but in a rather different way than you're considering.
Actually, there are two different ways: one is to build a constructor for the contained class that 1) can be used implicitly, and 2) requires only the subset of elements that you really need for comparison. With this in place, you can do a search for foods.find(2);. You will end up creating a temporary object from 2, then finding that temporary object, but it will be a true temporary. Your code won't have to deal with it explicitly (anywhere).
Edit: What I'm talking about here would be creating an instance of the same type as you're storing in the map, but (possibly) leaving any field you're not using as a "key" un-initialized (or initialized to something saying "not present"). For example:
struct X {
int a; // will be treated as the key
std:::string data;
std::vector<int> more_data;
public:
X(int a) : a(a) {} // the "key-only" ctor
X(int a, std::string const &d, std::vector<int> const &m); // the normal ctor
};
std::set<X> s;
if (s.find(2)) { // will use X::X(int) to construct an `X`
// we've found what we were looking for
}
Yes, when you construct your X (or what I've called X, anyway) with the single-argument constructor, chances are that what you construct won't be usable for anything except searching.
end edit]
The second, for which the library provides more direct support is often a bit simpler: if you're really only using some subset of elements (perhaps only one) for searching, then you can create a std::map instead of std::set. With std::map, searching for an instance of whatever you've specified as the key type is supported explicitly/directly.
key_type is a member type defined in set containers as an alias of Key, which is the first template parameter and the type of the elements stored in the container.
See documentation.
For user-defined types there is no way for the library to know what the key type is. It so happens that for your particular use case the key type is int. If you use a set< int > s; you can call s.find( 2 );. However, you will need to help the compiler out if you want to search a set< foo > and want to pass in only an integer (think how will the set's ordering work between foo and an int).
Because if you want to do std::find(2) you'll have to define how int will compare with foo in addition to the comparison between two foos. But since int and foo are different types, you will need actually two additional functions:
bool operator<(int, foo);
bool operator<(foo, int);
(or some other logical equivalent pair).
Now, if you do that, you are actually defining a bijective function between int and foo and you could as well simply use a std::map<int, foo> and be done.
If you still don't want the std::map but you want the benefits of a sorted search, then the dummy object is actually the easiest way to go.
Granted, the standard std::set could provide a member function, to which you pass a function that receives a foo and return -1, 0, 1 if it is less, equal or greater than the searched one... but that's not the C++ way. And note that even bsearch() takes two arguments of the same type!