Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why `S x({})` invoke default constructor in GCC 7/C++1z mode only?

In the following snippet, GCC 7 with C++1z mode invokes the default constructor, but GCC/C++14 and Clang/C++14,C++1z invoke the initializer-list constructor.

Is this behavior affected by any C++1z Specifiation change (possibly Guaranteed copy elision?), or GCC bug?

#include <cstdio>
#include <initializer_list>

struct S {
  S() { std::printf("DEF "); }      // (1)
  S(std::initializer_list<int> il)  // (2)
    { std::printf("L=%zu ", il.size()); }
};

int main() {
  S x({});
}

Output:

  • gcc 7.1.0/-std=c++14: L=0
  • gcc 7.1.0/-std=c++1z: DEF
  • Clang HEAD 5.0.0/-std=c++14 and c++1z: L=0
like image 303
yohjp Avatar asked May 17 '17 04:05

yohjp


1 Answers

I think this is a gcc bug (submitted as 80804). The order of rules for [dcl.init] in C++17 is:

If the destination type is a (possibly cv-qualified) class type:

  • If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.

That first bullet does not apply. The initializer expression here is {}, which isn't even an expression so it doesn't even have a cv-unqualified type to compare against S. This bullet would apply if we had written S x(S{}) instead.

  • Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution. The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

This is direct-initialization, so constructors are considered as per [over.match.ctor], which just tells is to overload on the constructors. Since there is a std::initializer_list constructor, that one gets priority per [over.ics.rank], so that one is selected.


The only difference between C++14 and C++17 here is the introduction of that first bullet - which doesn't apply anyway, so the behavior should be the same.

like image 164
Barry Avatar answered Nov 03 '22 21:11

Barry