'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), where p is the name of the corresponding constructor parameter and T is the declared type of p.

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