问题
The Problem
I have a std::map<int, std::set<int>>
named misi
. I'm wondering why misi.emplace(2345, {6, 9});
and misi.emplace({2345, {6, 9}});
don't work as expected, as shown below.
The Code
#include <set> // std:set
#include <map> // std::map
#include <utility> // std::piecewise_construct, std::pair
#include <tuple> // std::forward_as_tuple
#include <iostream> // std::cout, std::endl
int main()
{
// --- std::set initializer list constructor ---
std::set<int> si({42, 16});
std::cout << "si.size(): " << si.size() << std::endl; // 2
std::cout << "*si.begin(): " << *si.begin() << std::endl; // 16
// --- std::set emplace() ---
si.emplace(7);
std::cout << "si.size(): " << si.size() << std::endl; // 3
std::cout << "*si.begin(): " << *si.begin() << std::endl; // 7
std::cout << "--------" << std::endl;
Above is std::set
, you can see that the initializer list constructor and emplace()
work perfectly.
// --- std::map initializer list constructor ---
std::map<int, int> mii({ {0, 42}, {1, 16} });
std::cout << "mii.size(): " << mii.size() << std::endl; // 2
std::cout << "mii[0]: " << mii[0] << std::endl; // 42
std::cout << "mii[1]: " << mii[1] << std::endl; // 16
// --- std::map emplace() ---
mii.emplace(1234, 7);
std::cout << "mii.size(): " << mii.size() << std::endl; // 3
std::cout << "mii[1234]: " << mii[1234] << std::endl; // 7
// --- std::map emplace() with std::pair() ---
mii.emplace(std::pair<int, int>(2345, 6));
std::cout << "mii.size(): " << mii.size() << std::endl; // 4
std::cout << "mii[2345]: " << mii[2345] << std::endl; // 6
std::cout << "--------" << std::endl;
Above is std::map
of int
to int
, you can see the methods work perfectly too, except that in the last example, the std::pair
might be somehow redundant. I wonder if the std::pair
is constructed in-place or not. (Well, I guess not)
// --- std::map to std::set initializer list constructor ---
std::map<int, std::set<int>> misi({ {0, {42, 16}}, {1, {7}} });
std::cout << "misi.size(): " << misi.size() << std::endl; // 2
std::cout << "*misi[0].begin(): " << *misi[0].begin() << std::endl; // 16
std::cout << "*misi[1].begin(): " << *misi[1].begin() << std::endl; // 7
For a std::map
to a std::set
, the initializer list constructor works perfectly as shown above. But the emplace()
does not! (as shown below)
// --- Compilation Errors ---
//misi.emplace(2345, 6, 9);
//misi.emplace({2345, 6, 9});
//misi.emplace(2345, {6, 9});
//misi.emplace({2345, {6, 9}});
//misi.emplace(
// std::piecewise_construct,
// std::forward_as_tuple(2345),
// std::forward_as_tuple(6, 9)
//);
//misi.emplace(
// std::piecewise_construct,
// std::forward_as_tuple(2345),
// std::forward_as_tuple({6, 9})
//);
Here, the following syntax is okay, but doesn't do exactly what I wanted:
// --- OK ---
misi.emplace(std::pair<int, std::set<int>>(2345, {6, 9}));
std::cout << "misi.size(): " << misi.size() << std::endl; // 3
std::cout << "*misi[2345].begin(): " << *misi[2345].begin() << std::endl; //6
std::cout << "--------" << std::endl;
return 0;
}
So, it seems that there is no way to create the std::pair
in-place, and it seems that the std::set
is created in-place (right?). Does anyone have any ideas?
The compiler I'm using is:
$ clang++ --version
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix
回答1:
Braced initializer lists have no type, so they can't be perfectly forwarded. In this particular case, you can specify the type (std::initializer_list<int>
) explicitly if you want everything to be constructed in place:
misi.emplace(
std::piecewise_construct,
std::forward_as_tuple(2345),
std::forward_as_tuple(std::initializer_list<int>{6, 9})
);
Since only one argument each is passed for the key and the value, and the initializer_list
constructor of std::set
is not explicit
, you can remove the piecewise_construct
altogether and have emplace
construct the pair
using the constructor taking two arguments:
misi.emplace(
2345,
std::initializer_list<int>{6, 9}
);
mii.emplace(std::pair<int, int>(2345, 6));
I wonder if the std::pair is constructed in-place or not. (Well, I guess not)
No, a temporary std::pair<int, int>
is constructed and passed to emplace
, which constructs an instance of the map
's value_type
(which is pair<const int, int>
) with it. The latter is what is actually stored in the map
.
misi.emplace(std::pair<int, std::set<int>>(2345, {6, 9}));
it seems that the std::set is created in-place (right?).
No. Again, this creates a temporary std::pair<int, std::set<int>>
, and then constructs what's actually stored in the map
(which is pair<const int, std::set<int>>
) with it. This second construction will perform a move from the set<int>
stored in the temporary.
来源:https://stackoverflow.com/questions/28634223/how-to-use-emplace-in-a-stdmap-whose-value-is-a-stdset-map-from-something