'Inheriting a class and re-using its constructors issue missing in the base class
In the following program, struct B
has two user-defined constructors: one from int
and another from int&&
(clearly this is not very practical). And an object of B
is created from an l-value, which should unambiguously select the first constructor. Then struct C
is derived from B
inheriting its constructor by the using
-declaration, and an object of C
is created from the very same l-value:
struct B {
B(int) {}
B(int&&) {}
};
int i = 1;
B b(i); //ok everywhere
struct C : B {
using B::B;
};
C c(i); //ok in Clang only
MSVC prints rather strange error in the last line:
source>(10): error C2668: 'B::B': ambiguous call to overloaded function
<source>(3): note: could be 'B::B(int &&)'
<source>(2): note: or 'B::B(int)'
<source>(10): note: while trying to match the argument list '(int)'
despite l-value argument.
GCC error seems to agree and explains more:
source>:13:6: error: use of deleted function 'C::C(int) [inherited from B]'
13 | C c(i);
| ^
<source>:10:14: note: 'C::C(int) [inherited from B]' is implicitly deleted because the default definition would be ill-formed:
10 | using B::B;
| ^
<source>:10:14: error: call of overloaded 'B(int)' is ambiguous
At the same time Clang accepts the code just fine, demo: https://gcc.godbolt.org/z/oGjzrYEhz
Which compiler is right here?
Solution 1:[1]
In C++14, GCC is right to reject C c(i)
. According to C++14: class.inhctor#8
An implicitly-defined inheriting constructor performs the set of initializations of the class that would be performed by a user-written inline constructor for that class with a mem-initializer-list whose only mem-initializer has a mem-initializer-id that names the base class denoted in the nested-name-specifier of the using-declaration and an expression-list as specified below, and where the compound-statement in its function body is empty ([class.base.init]). If that user-written constructor would be ill-formed, the program is ill-formed. Each expression in the expression-list is of the form
static_cast<T&&>(p)
, wherep
is the name of the corresponding constructor parameter andT
is the declared type ofp
.
the struct C
definition is equivalent to
struct C : B {
C(int p) : B(static_cast<int&&>(p)) {}
C(int&& p) : B(static_cast<int&&>(p)) {}
};
where the call B(static_cast<int&&>(p))
is indeed ambiguous.
And in C++17 standard the wording has changed C++17: class.inhctor.init#1, and Clang is right to accept the program according to new rules.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Fedor |