emplace_back与std::vector<std::map<int, int>>不兼容,无法正常工作。

25

我试图将emplace_back插入到一个std::vector<std::map<int, int>>中,但找不到正确的语法来实现它。

#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正常工作?


没有任何内容。使用大括号和小括号。 - g-217
我认为我们不应该将构造的对象传递给emplace_back。 - g-217
v.emplace_back( std::map<int, int>{{1,2}} ); .. 当然不是理想的。 - M.M
@M.M:这样做失去了使用emplace_back的优势,因此等同于push_back。 - galinette
@GautamJha:你可以将一个构造好的对象传递给emplace_back,它会使用被添加对象的复制或移动构造函数,这等同于使用push_back。 - galinette
看起来与这个相关:https://dev59.com/RWoy5IYBdhLWcg3wFqJT - galinette
3个回答

13
emplace_back将所有参数转发到成员类型的匹配构造函数中。现在,std::map具有初始化列表构造函数,但它需要一个std::pair<const Key, Value>的列表,即std::pair<const int, int>。由于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;
}

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


其实不是这样的,这个确实是有道理的。只是在转发方面会有一点复杂,但你可以随时写一个标准的提案来解决这个问题。那样会更加有效率。 - Klemens Morgenstern
不。我已经回答并拒绝了那个想法。除了我已经多次为标准做出过贡献之外,真正修复C++的混乱局面的唯一方法是替换它,而这已经完成了。 - Lightness Races in Orbit
@LightnessRacesinOrbit:您是在指哪个申请者? - Deduplicator
@Deduplicator:我不具体说明! - Lightness Races in Orbit
@LightnessRacesinOrbit:谢谢你让我笑了。你确定你还没有停留在C++里吗? - Deduplicator
显示剩余5条评论

7
一个可以用以下帮助函数实现:
 #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} );

那么它就可以完美地编译。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接