Search code examples
c++c++11vectorg++initializer-list

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


This is the simplified code:

#include <vector>

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

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

and compiling with g++ 5.2.1 a get this error:

 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&&)

When I saw the compiler error I found out that I have written {{}} by mistake (yeah, don't be mean to me), but I still cannot understand the error. IMHO either the compiler must get rid of the extra {} or return a syntax error.

Then I tried to compile this:

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

which works as intended.


Solution

  • But std::vector<int> v = {{{}}}; doesn't do what you think; it initializes a vector with one int element, initialized to zero. This is because int can be list-initialized:

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

    So std::vector<int> v = {{{}}}; is parsed as:

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

    Likewise, if you write

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

    the contained vector<int> has 1 element, initialized to zero. (The initializer_list<int> stage can be omitted because the vector(initializer_list<int>) constructor is non-explicit).

    So VInitList vil({{}}); could be parsed as:

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

    or as

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

    In the first case the vector has 1 element; in the second case it's empty. It's just as well that gcc rejects your code.

    Clang only parses it as the former; I'm not sure which is correct.