I think this program is a minimal example of your problem:
#include <iostream>
struct T {};
struct A {
A(T) {}
};
struct B {
B(T) {}
};
struct C {
C(A const&) { std::cout << "C(A)\n"; }
C(B const&) { std::cout << "C(B)\n"; }
};
int main() {
C c{T{}};
}
You have two types A
and B
both implicitly convertible from another type T
, and another type C
implicitly convertible from A
and B
, but for which implicit conversion from T
is ambiguous. You desire to disambiguate the situation so that C
is implicitly convertible from T
using the sequence of conversions T => A => C
, but you must do so without changing the definitions of A
and B
.
The obvious solution - already suggested in the comments - is to introduce a third converting constructor for C
: C(T value) : C(A(value)) {}
. You have rejected this solution as not general enough, but without clarifying what the "general" problem is.
I conjecture that the more general problem you want solved is to make C
unambiguously implicitly convertible from any type U
that is implicitly convertible to A
using the sequence of conversions U => A => C
. This is achievable by introducing an additional template constructor to C
(Live code demo at Coliru):
template <typename U, typename=typename std::enable_if<
!std::is_base_of<A,typename std::decay<U>::type>::value &&
std::is_convertible<U&&, A>::value>::type>
C(U&& u) : C(A{std::forward<U>(u)}) {}
The template constructor is a direct match for C(U)
, and so is unambiguously preferred over the C(A)
and C(B)
constructors that would require a conversion. It is constrained to accept only types U
such that
U
is convertible toA
(for obvious reasons)U
is notA
or a reference toA
or a type derived fromA
, to avoid ambiguity with theC(const A&)
constructor and infinite recursion in the case thatU
is e.g.A&
orA&&
.
Notably this solution does not require changing the definitions of T
, A
, B
, C(A const&)
or C(B const&)
, so it is nicely self-contained.