Curious Curious - 3 months ago 18
C++ Question

Clang skips processing of an initializer list construction

If you look at the following code, I think that both the lines in

main()
should call the
initializer_list
constructor for
InitSomething
. gcc outputs
22
as I expected, but clang just outputs a single
2
. Is clang wrong?

I am compiling with
-std=c++14
.

#include <iostream>

struct InitSomething {
explicit InitSomething(int) { std::cout << '1'; }
InitSomething(std::initializer_list<int> ) { std::cout << '2'; }
operator int() { return 1; }
};

int main() {
InitSomething init_something{1};
InitSomething init_something_else{init_something};
}





The output of
clang++ --version
(I am on a mac) is

Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin


and the output of
g++ --version
on the other platform I mentioned is

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Answer

This is a gcc bug (just submitted 76262). This:

InitSomething init_something_else{init_something};

invokes a constructor of InitSomething (irrespective of core defect report 1467). The viable options are:

InitSomething(InitSomething const&);        // (1) implicit copy constructor
explicit InitSomething(int );               // (2) via operator int()
InitSomething(std::initializer_list<int> ); // (3) via operator int()

(2) and (3) involve a user-defined conversion sequence, (1) is a standard conversions sequence (with rank exact match), so it should be the best viable candidate. This is what clang does correctly.

Now, the wording that favors std::initializer_list is in [over.ics.rank]:

Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of the following rules applies:
(3.1) — List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if
(3.1.1) — L1 converts to std::initializer_list<X> for some X and L2 does not, or, if not that,
(3.1.2) — L1 converts to type “array of N1 T”, L2 converts to type “array of N2 T”, and N1 is smaller than N2,
even if one of the other rules in this paragraph would otherwise apply.

The key bit here is that the conversion sequences must be of the same form. But they're not. The conversion sequence for (1) is a standard conversion sequence and the conversion sequences for (2) and (3) are user-defined conversion sequences. So this rule doesn't apply. Hence, the correct choice is (1).

Comments