嵌套初始值设定项列表的列表模糊性,每个列表包含一个项目



我正在编写一个置换类,我想使用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。编译器弄不清楚该使用哪个构造函数。我可以凑合着忽略这些情况,因为在我的特定用例中,如果列表列表中的每个列表都由单个元素组成,那么循环符号就是标识。然而,我想知道如何防止这种隐含的对话在未来发生。这将如何实现?

正如您所说,您的示例中的{1}{2}可以通过直接列表初始化转换为int 1int 2,因此会出现歧义。例如,n3337中的8.5.4(C++标准草案(规定了

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。例如,以下代码使用嵌套的初始值设定项列表{ { int{1} }, { int{2} }}:调用SomeClass的第二个构造函数

SomeClass({ {{1}}, {{2}} });

OTOH,下面的代码用嵌套的初始化器列表{ { int{1}, int{2} } }:调用第二个构造函数

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。编译器不知道构造函数使用的是哪个版本,因为两者都是正确的。我认为这可能是个问题。在c++11中,您可以使用以下初始化:int a {1};

最新更新