重载调用是不明确的:一对内联映射作为构造函数参数

Overloaded call is ambiguous: one-pair inline map as constructor argument

本文关键字:映射 参数 构造函数 调用 不明确 重载      更新时间:2023-10-16

我有一个类——大致类似于下面的类——它将映射作为其唯一构造函数的唯一参数。

#include <iostream>
#include <map>
using namespace std;
class Dict {
public:
Dict (map<int, int> contents) {
elements = contents;
}
int getElement (int i) {
return elements[i];
}
map<int, int> elements;
};
int main() {
Dict* test0 = new Dict({{1, 2}, {3, 4}});    /* Succeeds */
Dict* test1 = new Dict({{1, 2}});            /* Fails */
}

如上面的注释中所述,第一个构造函数不会抛出错误;它与这样的答案一致。不明确的调用错误如下:

main.cpp:43:36: error: call of overloaded 'Dict()' is ambiguous
Dict* test1 = new Dict({{1, 2}});            /* Fails */
^
main.cpp:16:5: note: candidate: Dict::Dict(std::map)
Dict (map<int, int> contents) {
^
main.cpp:14:7: note: candidate: Dict::Dict(const Dict&)
class Dict {
^
main.cpp:14:7: note: candidate: Dict::Dict(Dict&&)

如果映射中的键和值是不同类型的(例如,如果Dict()将整数映射到布尔值并且我调用new Dict({{1, true}})(,则不会出现此错误,并且代码按预期工作。

这个单一的构造函数是如何模棱两可的?为什么在两个相同类型的对象之间有一个映射的情况下,它特别不明确?香草C++有什么明显的解决方法吗?

这主要是由std::map的构造函数引起的:

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

即使参数不是迭代器,也会启用此构造函数,从而参与重载解析。结果,

{1, 2}   -> std::map<int, int>
{{1, 2}} -> std::map<int, int>    

都是有效的转化,这意味着

{{1, 2}} -> Dict
{{1, 2}} -> std::map<int, int> 

都是有效的转换。因此,Dict的三个构造函数是模棱两可的:

Dict(map<int, int>);
Dict(const Dict&);
Dict(Dict&&);

对于new Dict({{1, true}})的情况,InputIterator不能被正确地推导出来,因此不再有歧义。

您可以明确Dict(map<int, int>);,或使用Ben Voigt建议的三对大括号。


为什么三副牙套可以工作?

因为在这种情况下,对于复制/移动构造函数候选项,不允许用户定义的转换。这在 [over.best.ics]/4 中明确说明(我不相关的部分被省略了(:

但是,如果目标是

  • 构造函数的第一个参数或

并且构造函数或用户定义的转换函数是候选者

  • 。或

  • [over.match.list] 的第二阶段,当初始值设定项列表只有一个元素本身就是初始值设定项列表,并且目标是类 X 构造函数的第一个参数,并且转换为 X 或引用 cv X,

不考虑用户定义的转换序列。