C++隐式转换生成子类实例后能否通过父类引用传递完成构造
结论
你想要的D d5{b}写法无法直接通过当前C++标准的隐式转换规则实现,核心原因和转换查找的范围限制有关。
标准规则依据
根据C++标准[class.conv.general]/4的规定:隐式转换序列中最多允许出现一次用户定义的转换(转换构造函数或自定义转换运算符)。
而你遇到的核心限制不止于此:当你需要将B类型的实参传入形参为A const&的D构造函数时,编译器查找可用用户定义转换的范围仅限两类:
- 源类型
B中定义的、返回值可直接绑定到A const&的转换运算符(即B::operator A() const这类) - 目标类型
A中定义的、接收B const&参数的转换构造函数(即A::A(B const&)这类)
你的代码里的C::C(B const&)是B到C的转换,不属于上述两类范围。编译器不会自动推导B→C→A const&的转换链:因为从B到A的用户定义转换不存在,派生类到基类的标准转换不能用来拼接出跨类型的转换路径,否则重载决议的计算复杂度会出现不可控的膨胀,这是C++标准明确禁止的行为。
你之前的理解是正确的:派生类到基类的引用绑定属于标准转换,不计入用户定义转换的次数,但问题是这条转换路径从一开始就不在编译器的查找范围内。
可行的替代方案
如果你可以修改D的定义,同时不想让D感知到C等具体A子类的存在,可以给D加一个泛型模板构造函数,把转换逻辑放到函数内部(此时就不受隐式转换序列的规则限制):
#include <type_traits> #include <utility> struct A {}; struct B {}; struct C : public A { C() = default; C(B const& b) {} }; // 类型萃取,用来映射可转换为A子类的外部类型到对应的子类类型 template<typename T> struct a_child_mapping : std::false_type {}; // 给B添加映射,对应A的子类C,后续新增其他类型只需加这个偏特化即可,不需要改D的代码 template<> struct a_child_mapping<B> : std::true_type { using type = C; }; struct D : public A { D(A const& input) {} // 泛型构造函数,自动匹配所有有映射的外部类型 template<typename T> requires a_child_mapping<std::decay_t<T>>::value D(T&& t) : D(typename a_child_mapping<std::decay_t<T>>::type(std::forward<T>(t))) {} }; int main () { B b{}; D d5{b}; // 现在可以正常编译运行 };
这个方案完全满足你的需求:
- 不需要给D新增接收
C const&的构造函数,D不需要感知任何A子类的具体实现 - 用户不需要手动加
static_cast,也不需要感知C的存在 - 后续新增其他类似B的外部类型,只需要新增
a_child_mapping的偏特化即可,不需要修改现有上层代码。
内容的提问来源于stack exchange,提问作者Vyas
相关产品推荐
相关产品推荐

