std::map 个元组和使用 emplace
std::map of tuple to tuple and using emplace
考虑以下代码,使用 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});
}
我原以为 map::emplace()
的 initializer_list
参数就足够了,并且会按照指定将元组键插入到元组值关联中。显然,编译器不同意。
当然显式创建元组(即 ThreeTuple{1,2,3}
而不是 {1,2,3}
)并将其传递给 map::emplace()
可以解决问题,但为什么初始化列表不能直接传递给 map::emplace()
会自动将它们转发给元组构造函数?
but why can't the initializer lists be passed directly to map::emplace()
因为初始化列表不是表达式,所以它们没有类型。 emplace()
的签名只是:
template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );
而且你无法从 {1,2,3}
中推断出类型。你不能在 C++11 中,你仍然不能在 C++1z 中。此规则的唯一例外是模板参数的形式为 std::initializer_list<T>
,其中 T
是模板参数。
为了使 m.emplace({1,2,3},{4,5,6});
正常工作,您需要像这样的签名:
std::pair<iterator,bool> emplace(key_type&&, mapped_type&&);
据我所知,在这种情况下,C++17 中的任何更改都无关紧要。正如 NathanOliver 和 Barry 所解释的,{1,2,3}
不能被推断为具有任何类型,因此不能与模板参数匹配。您必须为 ThreeTuple
的构造函数提供参数作为可推导类型,即
m.emplace(std::piecewise_construct,
std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
调用构造函数
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...>);
在这种特殊情况下,您甚至可以省略 std::piecewise_construct
m.emplace(std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
或(在 C++17 中,正如 Nicol 在评论中指出的那样)
m.emplace(std::tuple(1,2,3), std::tuple(4,5,6));
相当于
m.emplace(ThreeTuple(1,2,3), ThreeTuple(4,5,6));
并调用构造函数
template<typename T1, typename T2>
std::pair<T1,T2>::pair(const&T1, const&T2);
另请注意,据我所知,您无法通过明确使用 std::initializer_list<int>
来实现此功能。原因很简单,pair<ThreeTuple,ThreeTuple>
(地图的 value_type
)没有合适的构造函数。
类似的东西可以在 C++17
:
m.try_emplace({1,2,3},4,5,6);
考虑以下代码,使用 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});
}
我原以为 map::emplace()
的 initializer_list
参数就足够了,并且会按照指定将元组键插入到元组值关联中。显然,编译器不同意。
当然显式创建元组(即 ThreeTuple{1,2,3}
而不是 {1,2,3}
)并将其传递给 map::emplace()
可以解决问题,但为什么初始化列表不能直接传递给 map::emplace()
会自动将它们转发给元组构造函数?
but why can't the initializer lists be passed directly to
map::emplace()
因为初始化列表不是表达式,所以它们没有类型。 emplace()
的签名只是:
template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );
而且你无法从 {1,2,3}
中推断出类型。你不能在 C++11 中,你仍然不能在 C++1z 中。此规则的唯一例外是模板参数的形式为 std::initializer_list<T>
,其中 T
是模板参数。
为了使 m.emplace({1,2,3},{4,5,6});
正常工作,您需要像这样的签名:
std::pair<iterator,bool> emplace(key_type&&, mapped_type&&);
据我所知,在这种情况下,C++17 中的任何更改都无关紧要。正如 NathanOliver 和 Barry 所解释的,{1,2,3}
不能被推断为具有任何类型,因此不能与模板参数匹配。您必须为 ThreeTuple
的构造函数提供参数作为可推导类型,即
m.emplace(std::piecewise_construct,
std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
调用构造函数
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...>);
在这种特殊情况下,您甚至可以省略 std::piecewise_construct
m.emplace(std::forward_as_tuple(1,2,3),
std::forward_as_tuple(4,5,6));
或(在 C++17 中,正如 Nicol 在评论中指出的那样)
m.emplace(std::tuple(1,2,3), std::tuple(4,5,6));
相当于
m.emplace(ThreeTuple(1,2,3), ThreeTuple(4,5,6));
并调用构造函数
template<typename T1, typename T2>
std::pair<T1,T2>::pair(const&T1, const&T2);
另请注意,据我所知,您无法通过明确使用 std::initializer_list<int>
来实现此功能。原因很简单,pair<ThreeTuple,ThreeTuple>
(地图的 value_type
)没有合适的构造函数。
类似的东西可以在 C++17
:
m.try_emplace({1,2,3},4,5,6);