Reputation: 101
Here is my custom class, which I am using as a key. Next is the value, which I am using as vector. While I am trying to use uniform initialization, I get the compilation error. I am unable to understand what that error really means? Here is the error and the code.
Based on the advise by somebody on insert - How to use insert_or_assign in map for custom class?
I am using the similar thing for emplace(). However, it is not working.
The prototype of emplace says -
https://en.cppreference.com/w/cpp/container/map/emplace
template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );
It means the Args && is nothing but rvalue reference of class Args right?
In my case, the arg is nothing but std::vector. How can I do emplace?
class MyData
{
private:
int age;
string name;
public:
int getAge() const
{
return age;
}
string& getName()
{
return name;
}
MyData(int age_val, string name_val): age(age_val), name(name_val)
{
cout<<"Constructor invoked"<<endl;
}
bool operator <(const MyData &other) const
{
return age < other.age;
}
MyData(const MyData& other)
{
cout<<"Copy constructor invoked"<<endl;
age = other.age;
name = other.name;
}
};
int main(int argc, char **argv)
{
std::map<MyData, vector<string>> studentClasses;
studentClasses.emplace({32, "SJ"s}, std::vector{"working"s});
return 0;
}
In function ‘int main(int, char**)’:
map.cpp:62:63: error: no matching function for call to ‘std::map<MyData, std::vector<std::__cxx11::basic_string<char> > >::emplace(<brace-enclosed initializer list>, std::vector<std::__cxx11::basic_string<char> >)’
62 | studentClasses.emplace({32, "SJ"s}, std::vector{"working"s});
574 | emplace(_Args&&... __args)
| ^~~~~~~
/usr/include/c++/9/bits/stl_map.h:574:2: note: candidate expects 0 arguments, 2 provided
Upvotes: 3
Views: 1897
Reputation: 173024
It means the Args && is nothing but rvalue reference of class Args right? In my case, the arg is nothing but std::vector. How can I do emplace?
Note that both key and value are passed to emplace
to constructor the element in map
. (BTW Args&&
is not rvalue reference but forwarding reference.)
The problem is that the braced-init-list like {32, "SJ"s}
doesn't have type, which causes template argument deduction failing when calling emplace
.
- The parameter P, whose A is
a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or (since C++17)
a reference to an array:
You can pass a MyData
explicitly:
studentClasses.emplace(MyData{32, "SJ"s}, std::vector{"working"s});
Or specify template argument as:
studentClasses.emplace<MyData>({32, "SJ"s}, std::vector{"working"s});
Upvotes: 2
Reputation: 20171
I don't need it often but sometimes I need it: a "real" emplace()
in a std::map
. Most times it causes me some trouble until I get it working but most times I finally got.
My MCVE:
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std::string_literals;
struct MyData {
int age;
std::string name;
MyData(int age, std::string name): age(age), name(name)
{
std::cout << "MyData::MyData(int, std::string)\n";
}
MyData(const MyData& myData): age(myData.age), name(myData.name)
{
std::cout << "MyData::MyData(const MyData&)\n";
}
MyData(MyData&& myData) noexcept:
age(std::move(myData.age)), name(std::move(myData.name))
{
std::cout << "MyData::MyData(MyData&&)\n";
}
bool operator<(const MyData& myData) const
{
return age < myData.age
|| (age == myData.age && name < myData.name);
}
};
#define DEBUG(...) std::cout << "Exec: " #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(std::map<MyData, std::vector<std::string>> studentClasses);
DEBUG(studentClasses.emplace(MyData{32, "SJ"s}, std::vector{"working"s}));
DEBUG(studentClasses.emplace<MyData>({32, "SJ"s}, std::vector{"working"s}));
DEBUG(studentClasses.emplace(std::piecewise_construct,
std::forward_as_tuple(32, "SJ"s),
std::forward_as_tuple(std::vector{ "working"s })));
}
Output:
Exec: std::map<MyData, std::vector<std::string>> studentClasses;
Exec: studentClasses.emplace(MyData{32, "SJ"s}, std::vector{"working"s});
MyData::MyData(int, std::string)
MyData::MyData(MyData&&)
Exec: studentClasses.emplace<MyData>({32, "SJ"s}, std::vector{"working"s});
MyData::MyData(int, std::string)
MyData::MyData(MyData&&)
Exec: studentClasses.emplace(std::piecewise_construct, std::forward_as_tuple(32, "SJ"s), std::forward_as_tuple(std::vector{ "working"s }));
MyData::MyData(int, std::string)
I provided a move constructor. So, the first two emplace versions (copied from songyuanyao's answer) use that one. (Without a move constructor, emplace()
would use the copy constructor of MyData
instead – as pointed out by the OP.)
I used my last resort piecewise_construct
(which already worked as well in C++11).
This one does in fact the "real" emplace
(i.e. in-place construction).
Further reading: cppreference.com: std::map::emplace()
Upvotes: 4