【问题标题】:emplace_back not working with std::vector<std::map<int, int>>emplace_back 不适用于 std::vector<std::map<int, int>>
【发布时间】:2015-10-19 05:30:42
【问题描述】:

我正在尝试将emplace_back 转换为std::vector&lt;std::map&lt;int, int&gt;&gt;,但找不到正确的语法。

#include<map>
#include<vector>

int main()
{
    std::vector<std::map<int, int>> v;
    std::map<int,int> a {{1,2}};

    v.push_back({{1,2}});

    v.emplace_back({1,2});    // error
    v.emplace_back({{1,2}});  // error
    v.emplace_back(({1,2}));  // error
}

push_back 在这里有效,但emplace_back 无效。我怎样才能让emplace_back 工作?

【问题讨论】:

  • 没有。只使用大括号和圆括号
  • 我相信我们不会将构造对象传递给 emplace_back。
  • v.emplace_back( std::map&lt;int, int&gt;{{1,2}} ); ..当然不理想
  • @M.M :这失去了 emplace_back 的优势,因此等同于 push_back
  • @GautamJha :你可以将一个构造对象传递给emplace_back,然后它使用添加对象的复制或移动构造函数,这最终相当于使用push_back

标签: c++ c++11


【解决方案1】:

emplace_back 将所有参数转发给成员类型的匹配构造函数。现在,std::map 有一个初始化列表构造函数,但它需要一个 std::pair&lt;const Key, Value&gt; 的列表,即std::pair&lt;const int, int&gt;push_back 不是模板,因此它只需要一种类型,从而就地执行转换。也就是说,这里没有进行类型推导。

您需要明确声明您想要std::pair;以下应该有效:

#include<map>
#include<vector>

int main()
{
    std::vector<std::map<int, int>> v;

    v.emplace_back(std::initializer_list<std::pair<const int, int>>{
            {1,2},{3,4},{5,6}});

    return 0;
}

出于同样的原因,这不会编译:

    v.emplace_back({std::pair<const int,int>(1,2),
                    std::pair<const int,int>(3,4)});

这是因为,虽然大括号括起来的列表可能会产生一个初始化列表,但它不是必须的。它也可以是构造函数调用或类似的东西。所以,写

auto l = {std::pair<const int,int>(1,2),
          std::pair<const int,int>(3,4)};

l 生成一个初始化列表,但表达式本身可能以其他方式使用:

std::pair<std::pair<const int, int>, std::pair<const int, int>> p =
          {std::pair<const int,int>(1,2),
          std::pair<const int,int>(3,4)}

整个事情变得有点混乱。

基本上,如果你有一个大括号封闭列表,它可能会产生一个初始化列表或调用一个匹配的构造函数。在某些情况下,编译器无法确定需要哪些类型; emplace_back 就是其中之一(因为转发)。在其他情况下,它确实有效,因为所有类型都在表达式中定义。例如:

#include <vector>
#include <utility>

int main() 
{
    std::vector<std::pair<const int, int>> v = 
         {{1,2},{3,4},{5,6}};
    return 0;
}

现在它不起作用的原因是无法推断出任何类型。 IE。 emplace_back 试图推断输入类型的名称,但这是不可能的,因为大括号封闭列表有多种可以描述的类型。因此没有匹配的函数调用。

【讨论】:

  • 不是真的,它实际上是有道理的。只是转发有点复杂,但你总是可以写一个标准的提案来解决这个问题。这样会更有成效……
  • 你显然不了解 C++ 的类型系统。该问题可能有一个解决方案,即大括号封闭列表无法绑定到模板函数参数(这再次:有意义,因为它不是没有类型),但需要有人编写它。问题是,在 C++ 中,类型解析必须在编译器时发生,这就是它不起作用的原因。这并不愚蠢,因为它是使语言快速的设计选择。
  • 大声笑好吧,我不了解 C++ 的类型系统。你的普遍习惯是接受某人对一种语言的表现力的看法,然后把它变成对那个人对技术上如何构建语言的理解的无知的、完全不知情的抨击吗?多么为你难过。我建议先看看我的个人资料,然后再继续你的仇恨之旅。
  • 表现力也是一个非常主观的标准,这就是你说它愚蠢之后第二次说的。我只是说,当将表达式传递给函数时,表达式不一定会产生一种类型,会导致此类问题。要么通过一个新的模板表达式来解决这个问题,要么你会遇到这类问题。另一种可能性是始终将其转换为 std::initializer_list,但这会损失性能。所以我不明白你的意思是表达能力,除了“我不喜欢它”。
  • 是的,这是主观的。而且,主观上,我认为这很愚蠢。我可以帮你吗?
【解决方案2】:

可以使用如下的辅助函数来实现:

 #include <map>
 #include <vector>

 void emplace_work_around(
    std::vector<std::map<int, int>>& v,
    std::initializer_list<std::pair<const int,int>> && item
 )
 {
    v.emplace_back(std::forward<std::initializer_list<std::pair<const int,int>>>(item));
 }

int main()
{
    std::vector<std::map<int, int>> v;

    emplace_work_around(v,{{1,2}});
}

问题出在我们写的时候:

v.emplace_back({{1,2}});  // here {{1,2}} does not have a type.

编译器无法推断参数的类型,也无法决定调用哪个构造函数。

基本思想是,当你写一个像这样的函数时

template<typename T>
void f(T) {}

并像使用它

f( {1,2,3,4} ); //error

你会得到编译器错误,因为 {1,2,3,4} 确实有一个类型。

但是如果你定义你的函数为

template<typename T>
void f(std::initializer_list<T>) {}
 f( {1,2,3,4} );

然后完美编译。

【讨论】:

    【解决方案3】:

    如果我正确理解这些问题报告,这似乎目前不受支持:

    http://cplusplus.github.io/LWG/lwg-active.html#2089

    http://cplusplus.github.io/LWG/lwg-active.html#2070

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-26
      • 2014-09-20
      • 2011-10-29
      • 2014-05-12
      • 1970-01-01
      • 2013-07-11
      • 2010-11-18
      • 2017-01-12
      相关资源
      最近更新 更多