Consider the following code, compiled with g++ 7.0.1 (-std=c++17):
#include <map>
#include <tuple>
int main()
{
// Create an alias for a tuple of three ints
using ThreeTuple=std::tuple<int,int,int>;
// Create an alias for a map of tuple to tuple (of three ints)
using MapThreeTupleToThreeTuple=std::map<ThreeTuple,ThreeTuple>;
MapThreeTupleToThreeTuple m;
// The following does NOT compile
m.emplace({1,2,3},{4,5,6});
// ..., and neither does this
m.emplace(std::piecewise_construct,{1,2,3},{4,5,6});
}
I would have thought that the initializer_list
arguments to map::emplace()
would have sufficed and would have resulted in the insertion of the tuple key to tuple value association as specified. Apparently, the compiler disagrees.
Of course creating a tuple explicitly (i.e., ThreeTuple{1,2,3}
instead of just {1,2,3}
) and passing that to map::emplace()
solves the problem, but why can't the initializer lists be passed directly to map::emplace()
which would automatically forward them to the tuple constructors?
AFAIK, no changes in C++17 matter in this context. As explained by NathanOliver and Barry, {1,2,3}
cannot be deduced to have any type and hence cannot be matched against a template argument. You must provide the arguments for the constructor of ThreeTuple
as deducible types, i.e.
m.emplace(std::piecewise_construct,
std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
which calls the constructor
template<typename T1, typename T2>
template<typename... Args1, typename... Args2 >
std::pair<T1,T2>::pair(std::piecewise_construct_t,
std::tuple<Args1...>, std::tuple<Args2...>);
In this particular case, you can even omit the std::piecewise_construct
m.emplace(std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
or (in C++17 as pointed out by Nicol in a comment)
m.emplace(std::tuple(1,2,3), std::tuple(4,5,6));
which are equivalent to
m.emplace(ThreeTuple(1,2,3), ThreeTuple(4,5,6));
and call the constructor
template<typename T1, typename T2>
std::pair<T1,T2>::pair(const&T1, const&T2);
Note also that AFAIK you cannot get this working by using std::initializer_list<int>
explicitly. The reason is simply that there is not suitable constructor for pair<ThreeTuple,ThreeTuple>
(the value_type
of your map).
but why can't the initializer lists be passed directly to
map::emplace()
Because initializer lists aren't expressions and so they don't have types. The signature for emplace()
is just:
template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );
and you can't deduce a type from {1,2,3}
. You couldn't in C++11 and you still can't in C++1z. The only exception to this rule is if the template parameter is of the form std::initializer_list<T>
where T
is a template parameter.
In order for m.emplace({1,2,3},{4,5,6});
to work, you'd need a signature like:
std::pair<iterator,bool> emplace(key_type&&, mapped_type&&);
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