我有一个类——大致类似于下面的类——它将映射作为其唯一构造函数的唯一参数。
#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,
不考虑用户定义的转换序列。