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);