我正在编写一个Permutation类,我想使用initializer_list(一种形式的表示法)或嵌套的initializer_list(循环表示法)进行初始化。我遇到了一个过载消除歧义错误,为此我准备了以下最小示例:
#include <iostream>
#include <initializer_list>
class SomeClass{
public:
SomeClass(std::initializer_list<int> init)
{
std::cout << "Constructor 1" << std::endl;
}
SomeClass(std::initializer_list<std::initializer_list<int>> init)
{
std::cout << "Constructor 2" << std::endl;
}
};
int main()
{
SomeClass({{1},{2}});
return 0;
}
会产生以下编译器错误(为清楚起见进行了编辑):
nested_init.cpp: In function ‘int main()’: nested_init.cpp:23:22:
error: call of overloaded ‘SomeClass(<brace-enclosed initializer list>)’
is ambiguous SomeClass({{1},{2}});
nested_init.cpp:14:3: note: candidate:
SomeClass::SomeClass(std::initializer_list<std::initializer_list<int>>)
SomeClass(std::initializer_list<std::initializer_list<int>> init)
nested_init.cpp:9:3: note: candidate:
SomeClass::SomeClass(std::initializer_list<int>)
SomeClass(std::initializer_list<int> init)
nested_init.cpp:3:7: note: candidate:
constexpr SomeClass::SomeClass(const SomeClass&)
nested_init.cpp:3:7: note: candidate:
constexpr SomeClass::SomeClass(SomeClass&&)
玩了之后,我想我已经弄明白了为什么会这样。 {1}和{2}可以分别隐式转换为1和2。编译器对于随后使用哪个构造函数感到困惑。 我可以做,并忽略这些情况,因为在我的特定用例中,如果列表中的每个列表都由单个元素组成,则循环符号就是标识。但是,我想知道如何防止将来发生这种隐式对话。这将如何实现?
答案 0 :(得分:1)
正如您所说,示例中的{1}
和{2}
可通过 direct-list-initialization 和转换为int 1
和int 2
因此出现歧义。
例如,n3337(C ++标准草案)中的8.5.4声明了
Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; ... [ Example: int x1 {2}; // OK int x2 {2.0}; // error: narrowing — end example ]
避免这种歧义的一种简单方法是使用双括号{{...}}
并显式创建一个std::initializer_list
。
例如,以下代码使用嵌套的初始化程序列表SomeClass
调用{ { int{1} }, { int{2} }}
的第二个构造函数:
SomeClass({ { { int{1}, int{2} } }
, {{2}} });
OTOH,以下代码使用嵌套的初始化程序列表SomeClass({ { {1}, {2} } });
调用第二个构造函数:
// direct-list-initialization
SomeClass a{ {{1}}, {{2}} }; // Constructor 2 with a nested initializer_list { { int{1} }, { int{2} }}
SomeClass b{ { {1}, {2} } }; // Constructor 2 with a nested initializer_list { { int{1}, int{2} } }
// copy-list-initialization
SomeClass d = { {{1}}, {{2}} }; // Constructor 2 with a nested initializer_list { { int{1} }, { int{2} }}
SomeClass e = { { {1}, {2} } }; // Constructor 2 with a nested initializer_list { { int{1}, int{2} } }
直接列表初始化和复制列表初始化也可以这种方式工作:
{{1}}
答案 1 :(得分:0)
单个元素{1}
将自动转换为1
。编译器不知道使用哪个版本的构造器,因为两者都是正确的。我认为这可能是一个问题。在c ++ 11中,您可以使用以下初始化:int a {1};