C++ Logo

std-discussion

Advanced search

Re: inheriting initializer_list constructor fails with empty list

From: Svetlin Totev <svetli97_at_[hidden]>
Date: Tue, 28 May 2024 12:07:47 +0300
You are just repeating what I already mentioned the compiler output was.

> (3.5) — Otherwise, if the initializer list has no elements and T is a
class type
> with a default constructor, the object is value-initialized.

This point clearly states that it only applies to classes that have a
default constructor. But me, you, and the compilers agreed that B doesn't
have a default constructor, just like A doesn't have one. So this point is
irrelevant here.

And you missed my main point. Whatever rule makes "A a1 {};" work should
also make "B b1 {};" work as B inherits A's constructors.

>From the standard:
12.2.2.8 Initialization by list-initialization [over.match.list]
(1.1) If the initializer list is not empty or *T has no default constructor*,
overload resolution is first performed where 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.

The compilers (gcc, clang) correctly pick the initializer_list constructor
for B when the list is not empty, but miss the case where that constructor
should also be picked when the class has no default constructor. And yet
they apply this rule correctly for A. This is why I assumed it has to be
either a bug in both compilers or there is a special rule applied for the
line "using A::A;" that prevents the inherited constructors from working
according to the rules for initializer_list constructors. And yet they work
according to those rules in all cases except "B b1 {};".

Since two of the most popular compilers deviate from the standard in the
exact same way I assumed it was a weird issue with the standard itself.
That's why I came to ask about it here. But I can't find anything in the
standard to support this point. Following the standard's rules it is
fairly clear that the line "B b1 {};" should pick B's initializer_list
constructor before even looking for other constructors.

On Mon, May 27, 2024 at 6:05 PM Russell Shaw via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> On 28/5/24 00:16, Svetlin Totev via Std-Discussion wrote:
> > C++ allows "inheriting" constructors with a special rule for
> using-declarations.
> > It also has a special rule for constructors that take
> std::initializer_list
> > which changes the syntax for the constructor call.
> >
> > These two rules don't work consistently together. The special syntax for
> > constructors with std::initializer_list doesn't get inherited in all
> cases. In
> > the example below the line "B b1 {};" fails to compile because it tries
> to call
> > A() instead of A{}. Technically the constructors are inherited
> correctly. It's
> > the special syntax for initializer lists that doesn't get inherited in
> the edge
> > case of empty {} brackets. But it is inherited in all other cases.
> >
> > I'm not entirely sure if this is a language issue or a compiler one as I
> can't
> > fully comprehend the C++ standard but both gcc and clang would fail to
> compile
> > this example code and give the same reason.
> >
> > This language has too many special cases. Let's not have another one.
> Currently,
> > the struct C in the example code is a working workaround for the issue.
> ...
> >
> > Bb1 {}; // calls A() instead of A{} so it fails____
> >
> > Bb2 {1, 2, 3}; // ok____
>
> ---------------------------------------------
> ISO/IEC 14882:2020(E)
> 9.4.5 List-initialization [dcl.init.list]
>
> (3.5) — Otherwise, if the initializer list has no elements and T is a
> class type
> with a default constructor, the object is value-initialized.
> ---------------------------------------------
>
> ‘B::B()’ is implicitly deleted because the default definition would be
> ill-formed. There is no A::A() default constructor. That's my
> interpretation,
> anyway.
>
> g++ -DHAVE_CONFIG_H -I. -Wall -Wno-unused -Wno-unused-function
> -Wno-unused-label -Wno-unused-parameter -Wno-unused-value -Wno-parentheses
> -std=c++20 -Wno-address -Wpedantic -g -O0 -MT main.o -MD -
> MP -MF .deps/main.Tpo -c -o main.o main.cpp
> main.cpp: In function ‘int main()’:
> main.cpp:111:11: error: use of deleted function ‘B::B()’
> 111 | B b1 {}; // calls A() instead of A{} so it fails
> | ^
> main.cpp:85:8: note: ‘B::B()’ is implicitly deleted because the default
> definition would be ill-formed:
> 85 | struct B : A {
> | ^
> main.cpp:85:8: error: no matching function for call to ‘A::A()’
> main.cpp:82:5: note: candidate: ‘A::A(std::initializer_list<int>)’
> 82 | A(std::initializer_list<int> il) {};
> | ^
> main.cpp:82:5: note: candidate expects 1 argument, 0 provided
> main.cpp:81:8: note: candidate: ‘constexpr A::A(const A&)’
> 81 | struct A {
> | ^
> main.cpp:81:8: note: candidate expects 1 argument, 0 provided
> main.cpp:81:8: note: candidate: ‘constexpr A::A(A&&)’
> main.cpp:81:8: note: candidate expects 1 argument, 0 provided
> make[1]: *** [Makefile:430: main.o] Error 1
> make[1]: Leaving directory '/home/russell/AAProjs/GNU/bugs/g++'
> make: *** [Makefile:315: all] Error 2
> "main.cpp" 127 lines --0%--
> (9 of 28): error: use of deleted function ‘B::B()’
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>

Received on 2024-05-28 09:08:01