'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), wherepis the name of the corresponding constructor parameter andTis 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 |
