This is at the core of a Tensorflow issue.
Consider the following code:
class Option;
class Base
{
public:
explicit Base(Option opt);
};
class Derived : public Base
{
public:
using Base::Base;
explicit Derived(const Option &opt);
};
Derived
doit(const Option &opt)
{
return Derived(opt);
}
When compiled with GCC versions through 14.2, it fails like so:
<source>: In function 'Derived doit(const Option&)':
<source>:19:21: error: call of overloaded 'Derived(const Option&)' is ambiguous
19 | return Derived(opt);
| ^
<source>:6:12: note: candidate: 'Base::Base(Option)'
6 | explicit Base(Option opt);
| ^~~~
<source>:12:15: note: inherited here
12 | using Base::Base;
| ^~~~
<source>:13:12: note: candidate: 'Derived::Derived(const Option&)'
13 | explicit Derived(const Option &opt);
| ^~~~~~~
<source>:9:7: note: candidate: 'constexpr Derived::Derived(const Derived&)'
9 | class Derived : public Base
| ^~~~~~~
<source>:9:7: note: candidate: 'constexpr Derived::Derived(Derived&&)'
Compiler returned: 1
Clang accepts this code without complaint.
This seems like a bug either in Clang (for allowing an ambiguous call) or in GCC (for not allowing an unambiguous call). But I am not sure which. So, is this call ambiguous, or not?
The initialization is ambiguous, and clang++ is incorrect.
A rule which almost applies is [over.match.best] p7: In overload resolution, a viable function F1
is better than viable function F2
if
F1
is a constructor for a classD
,F2
is a constructor for a base classB
ofD
, and for all arguments the corresponding parameters ofF1
andF2
have the same type
But since the parameter types Option
and const Option&
are different, this rule should be ignored. No other rules in overload conversion distinguish between an inherited constructor and a plain member constructor. The implicit conversion sequences from a const Option
lvalue to parameter type Option
or to parameter type const Option&
are ambiguous, so Derived(opt)
is ill-formed.
This is already filed as an LLVM bug. Apparently a proposed fix is actively moving.