首页 > 解决方案 > C++ 构造函数与相同键/值类型的 std::map 不明确

问题描述

这是一个类定义作为示例。

#include <string>
#include <map>

template <class T>
class Collection
{
private:
  std::map<std::string, T> data;

public:
  Collection() {}

  Collection(std::map<std::string, T> d)
  {
    data = d;
  }
};

int这在使用s、chars 甚至vector模板类型初始化集合时效果很好。但是,当使用 a 初始化 astring并调用第二个重载构造函数时,例如:

Collection<std::string> col({
  { "key", "value" }
});

它不编译,并抛出此退出错误:

main.cpp:24:22: error: call to constructor of 'Collection<std::__cxx11::string>'
      (aka 'Collection<basic_string<char> >') is ambiguous
  Collection<string> col({
                     ^   ~
main.cpp:8:7: note: candidate constructor (the implicit move constructor)
class Collection
      ^
main.cpp:8:7: note: candidate constructor (the implicit copy constructor)
main.cpp:16:3: note: candidate constructor
  Collection(map<string, T> d)
  ^

奇怪的是,虽然这种表示法适用于其他类型,但这会中断,但这种表示法适用于string

Collection<std::string> col(std::map<std::string, std::string>({
  { "key", "value" }
}));

这里发生了什么?

标签: c++

解决方案


这是一个有趣的。

Amap可以由两个迭代器构造:

template<class InputIterator>
  map(InputIterator first, InputIterator last,
      const Compare& comp = Compare(), const Allocator& = Allocator());

值得注意的是,这个构造函数根本不需要检查它InputIterator是一个迭代器,更不用说取消引用它的结果是否可以转换为map的值类型。当然,实际上尝试构造映射会失败,但是要重载解析,map可以从任何两个相同类型的参数构造。

所以随着

Collection<std::string> col({
  { "key", "value" }
});

编译器看到两种解释:

  • 外大括号map使用map的初始化列表构造函数初始化 a,内大括号pair为该初始化列表构造函数初始化 a。
  • 外大括号初始化 a Collection,内大括号map使用“迭代器对”构造函数初始化 a 。

两者都是排名中的用户定义转换,两者之间没有决胜局,因此调用是模棱两可的 - 即使第二个,如果选择,会在map构造函数内部的某个地方导致错误。

当您在最外层也使用大括号时:

Collection<std::string> col{{
  { "key", "value" }
}};

标准中有一条特殊规则排除了第二种解释。


推荐阅读