g++ 和 clang 中的构造函数、初始化列表和不同行为

Constructor, initializer list and different behaviour in g++ and clang

这是简化代码:

#include <vector>

class VInitList
{
public:
    explicit VInitList(std::vector<int> v){}
};

int main()
{
    VInitList vil({{}});
}

并使用 g++ 5.2.1 编译时出现此错误:

 error: call of overloaded ‘VInitList(<brace-enclosed initializer list>)’ is ambiguous
     VInitList vil({{}});
                       ^
main.cpp:6:5: note: candidate: VInitList::VInitList(std::vector<int>)
     VInitList(std::vector<int> v){}
     ^
main.cpp:3:7: note: candidate: constexpr VInitList::VInitList(const VInitList&)
     class VInitList
           ^
main.cpp:3:7: note: candidate: constexpr VInitList::VInitList(VInitList&&)

当我看到编译错误时,我发现我写错了{{}}(是的,不要对我不好),但我仍然无法理解错误。恕我直言,编译器必须摆脱额外的 {} 或 return 语法错误。

然后我试着编译这个:

std::vector<int> v = {{{}}};

按预期工作。

但是std::vector<int> v = {{{}}};并没有按照你的想法去做;它用一个 int 元素初始化一个向量,初始化为零。这是因为 int 可以列表初始化:

int i{};   // assert(i == 0)

所以std::vector<int> v = {{{}}};被解析为:

std::vector<int> v = {{{}}};
                       ^-- int
                      ^-- initializer_list<int>
                     ^-- vector<int>

同样,如果你写

VInitList vil{{{}}};
               ^-- int
              ^-- vector<int>
             ^-- VInitList 

包含的 vector<int> 有 1 个元素,初始化为零。 (initializer_list<int>阶段可以省略,因为vector(initializer_list<int>)构造函数是非explicit)。

所以 VInitList vil({{}}); 可以解析为:

VInitList vil({{}});
               ^-- int
              ^-- vector<int>

VInitList vil({{}});
               ^-- vector<int>
              ^-- VInitList

在第一种情况下,vector 有 1 个元素;在第二种情况下它是空的。 gcc 拒绝您的代码也是一样。

Clang只解析为前者;我不确定哪个是正确的。