【问题标题】:std::map of tuple to tuple and using emplace元组到元组的 std::map 并使用 emplace
【发布时间】:2017-05-24 15:26:25
【问题描述】:

考虑以下代码,使用 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()自动将它们转发给元组构造函数?

【问题讨论】:

  • 初始化列表没有类型。 emplace 依赖于类型推导。你不能为无类型的东西推断出类型。
  • 您认为 C++17 在这个问题上发生了什么变化?
  • @Walter,我并不是要暗示我正在使用新的 C++17 功能,只是说我使用的是 C++17 兼容级别。

标签: c++ c++17


【解决方案1】:

AFAIK,在这种情况下,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);

另请注意,AFAIK 无法通过显式使用 std::initializer_list&lt;int&gt; 来实现此功能。原因很简单,pair&lt;ThreeTuple,ThreeTuple&gt;(地图的value_type)没有合适的构造函数。

【讨论】:

  • 有一个 +1 以展示如何正确执行此操作的工作示例。
  • 仅供参考:由于 OP 使用的是 C++17,因此您也可以使用模板类推导,因此只需使用 std::tuple(1, 2, 3)。假设编译器支持它并且标准库正确实现了它。
  • @NicolBolas 正确,但这与 OP 提到的 ThreeTuple(1,2,3) 几乎相同。无论如何,谢谢,我改变了答案。
  • 即使在 C++17 之前,这里使用 forward_as_tuplemake_tuple 相比也没什么好处。
【解决方案2】:

但是为什么初始化列表不能直接传递给map::emplace()

因为初始化列表不是表达式,所以它们没有类型。 emplace() 的签名只是:

template< class... Args >
std::pair<iterator,bool> emplace( Args&&... args );

你不能从{1,2,3} 中推断出类型。你不能在 C++11 中,你仍然不能在 C++1z 中。此规则的唯一例外是模板参数的格式为 std::initializer_list&lt;T&gt;,其中 T 是模板参数。

为了让m.emplace({1,2,3},{4,5,6}); 工作,您需要如下签名:

std::pair<iterator,bool> emplace(key_type&&, mapped_type&&);

【讨论】:

  • 带有std::piecewise_construct的版本呢?同样的问题?
  • @MichaelGoldshteyn 是的。它仍然只是一个emplace() 函数模板。
  • @MichaelGoldshteyn 请注意有initializer_list初始化列表initializer_list 可以从 initializer list 构造,因为它知道类型应该是什么。普通模板类型不会因此失败。
  • @NathanOliver,有趣的是,我认为我的 {1,2,3} 参数(会自动)导致推导出 std::initializer_list&lt;int&gt; 参数。
  • @MichaelGoldshteyn 只有在使用auto 时,它是特殊的。见:stackoverflow.com/questions/25612262/…
【解决方案3】:

类似的东西可以在C++17

m.try_emplace({1,2,3},4,5,6);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多