What is the correct behavior when using list-initialization to initialize a vector of structs that have an explicit constructor?

Viewed 115

On GCC 7.1, the below code compiles only if the explicit constructor has a default parameter. When it is run, the size of b is 1. Without a default parameter, there's a compilation error.

On Clang, the code compiles either way. When it is run, the size of b is 0. This is because an empty vector is copy constructed into b.

Both were tested using C++17.

#include <stdio.h>
#include <vector>
#include <iostream>

    struct TestStruct {
        TestStruct(){}
    };
    struct TestStructExplicit {
#if 1
        explicit TestStructExplicit(int x=0){}
#else
        explicit TestStructExplicit(int x){}
        explicit TestStructExplicit(){}
#endif
    };

int main()
{
    //note the nested braces
    std::vector<TestStruct> a({{}});
    std::vector<TestStructExplicit> b({{}});

    std::cout << "SIZE a " << a.size() << std::endl;
    std::cout << "SIZE b " << b.size() << std::endl;
}

Relevant godbolt link https://godbolt.org/z/s5banG54G with output for GCC and Clang.

What is the correct behavior here according to the standard? I think the C++17 standard paragraph below says that an explicit constructor cannot be used, so does that mean this is a GCC bug? -

16.3.1.7 Initialization by list-initialization [over.match.list] 1 When objects of non-aggregate class type T are list-initialized such that 11.6.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases: (1.1) — Initially, the candidate functions are the initializer-list constructors (11.6.4) of the class T and the argument list consists of the initializer list as a single argument. (1.2) — If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list. If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-listinitialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations (16.3.1.3, 16.3.1.4), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]

0 Answers
Related