I have found a discrepancy between the logic produced by gcc-8 and clang-6.
This happened in a real code base, when having developed using clang, I deployed using gcc.
Please kindly advise which compiler is in error so that I can file a bug appropriately.
A
is implicitly convertible to B
A
is constructible from both an A
(copy/move) and std::initializer_list<B>
.
When initialising an A
from an A&&
:
initializer_list
constructor.live demonstration: https://coliru.stacked-crooked.com/a/bc50bd8f040d6476
#include <initializer_list>
#include <utility>
#include <iostream>
struct thing;
struct thing_ref
{
thing_ref(thing&& other) : ref_(other) {}
thing_ref(thing& other) : ref_(other) {}
thing& ref_;
};
struct thing
{
thing() {}
thing(std::initializer_list<thing_ref> things)
{
std::cout << "initializer_list path\n";
}
thing(thing&& other)
{
std::cout << "move path\n";
}
thing(thing const& other)
{
std::cout << "copy path\n";
}
};
struct foo
{
foo(thing t) : mything { std::move(t) } {}
thing mything;
};
int main()
{
thing t;
auto f = foo { std::move(t) };
}
Nothing special, as per the coliru link: -std=c++17 -O2
Clang is designed as an API from its inception, allowing it to be reused by source analysis tools, refactoring, IDEs (etc) as well as for code generation. GCC is built as a monolithic static compiler, which makes it extremely difficult to use as an API and integrate into other tools.
GCC supports more traditional languages than Clang and LLVM, such as Ada, Fortran, and Go. GCC supports more less-popular architectures, and supported RISC-V earlier than Clang and LLVM. GCC supports more language extensions and more assembly language features than Clang and LLVM.
You don't need GCC to use Clang, as can be shown in the case of FreeBSD (they completely replaced GCC with Clang/LLVM and don't install GCC in the base anymore for licensing reasons). There are a variety of different C compilers other than GCC, it's just that GCC is the most common.
Clang is compatible with GCC. Its command-line interface shares many of GCC's flags and options. Clang implements many GNU language extensions and compiler intrinsics, some of which are purely for compatibility.
Standard draft (T
is thing
) [dcl.init.list]:
List-initialization is initialization of an object or reference from a braced-init-list. ...
List-initialization of an object or reference of type T is defined as follows:
If the braced-init-list contains a designated-initializer-list [does not apply]
If T is an aggregate class and [does not apply]
Otherwise, if T is a character array [does not apply]
Otherwise, if T is an aggregate [does not apply]
Otherwise, if the initializer list has no elements [does not apply]
Otherwise, if T is a specialization of
std::initializer_list<E>
[does not apply]Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution [applies]
...
[over.match.list]:
When objects of non-aggregate class type T are list-initialized such that [dcl.init.list] specifies that overload resolution is performed according to the rules in this subclause, overload resolution selects the constructor in two phases:
Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument. [applies]
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. [does not apply]
Back to [dcl.init.list] to find out what an initializer-list constructor is:
A constructor is an initializer-list constructor if its first parameter is of type
std::initializer_list<E>
or reference to possibly cv-qualifiedstd::initializer_list<E>
for some type E, and either there are no other parameters or else all other parameters have default arguments ([dcl.fct.default]).
There is also a handy note, that reasserts the conclusion:
Note: Initializer-list constructors are favored over other constructors in list-initialization
My conclusion:
The initializer-list constructor candidate should be considered first, and used if it is valid. As thing
implicitly converts to thing_ref
, it should be valid. It appears to me that GCC is conforming.
If you want to initialize an object of type that has an initializer-list constructor, but don't want to use that constructor, then don't use list initialization i.e. don't use a brace-init-list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With