You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让CRTP风格的指针别名模板适配C++类继承场景?

解决CRTP智能指针别名的继承冲突问题

问题描述

习惯在类内定义自身的智能指针别名以简化写法:

class A
{
public:
    using uptr = std::unique_ptr<A>;
    using sptr = std::shared_ptr<A>;
    using wptr = std::weak_ptr<A>;
};

通过A::uptr即可使用对应智能指针。为了复用逻辑,用CRTP模板实现通用化:

template <typename T>
class Ptrs
{
public:
    using uptr = std::unique_ptr<T>;
    using sptr = std::shared_ptr<T>;
    using wptr = std::weak_ptr<T>;
};

class A : public Ptrs<A>
{
};

但继承场景下出现两个问题:

  1. 子类B继承A后,B::uptr实际指向std::unique_ptr<A>,而非预期的std::unique_ptr<B>:
class B : public A
{
};

B::uptr b; // 类型为std::unique_ptr<A>,不符合需求
  1. 同时继承Ptrs<B>和A会触发编译错误,因为uptr等别名存在二义性:
class B : public Ptrs<B>, public A // 编译失败,别名重复定义
{
};

基于C++20环境,需要可行解决方案,并确认该做法的合理性。

可行解决方案

方案1:私有继承+using声明覆盖

通过私有继承Ptrs<B>避免外部访问冲突,再用using声明将当前类的智能指针别名暴露到public,覆盖父类的同名别名:

#include <memory>

template <typename T>
class Ptrs
{
public:
    using uptr = std::unique_ptr<T>;
    using sptr = std::shared_ptr<T>;
    using wptr = std::weak_ptr<T>;
};

class A : public Ptrs<A>
{
};

class B : private Ptrs<B>, public A
{
public:
    using Ptrs<B>::uptr;
    using Ptrs<B>::sptr;
    using Ptrs<B>::wptr;
};

此时B::uptr即为std::unique_ptr<B>,同时解决了二义性问题。

方案2:非侵入式全局模板别名(推荐)

放弃CRTP继承的方式,直接定义全局模板别名,完全避免继承带来的问题:

#include <memory>

template <typename T>
using uptr_t = std::unique_ptr<T>;
template <typename T>
using sptr_t = std::shared_ptr<T>;
template <typename T>
using wptr_t = std::weak_ptr<T>;

// 使用示例
uptr_t<A> a_ptr;
uptr_t<B> b_ptr;

该方案写法简洁,维护成本低,唯一区别是将A::uptr改为uptr_t<A>,功能完全一致。

方案3:C++20概念约束的CRTP改进

利用C++20概念约束模板使用范围,通过protected权限控制别名可见性,让派生类主动暴露自身的智能指针别名:

#include <memory>
#include <type_traits>

template<typename T>
concept DerivedFromPtrs = std::is_base_of_v<Ptrs<T>, T>;

template <DerivedFromPtrs T>
class Ptrs
{
protected:
    using uptr = std::unique_ptr<T>;
    using sptr = std::shared_ptr<T>;
    using wptr = std::weak_ptr<T>;
};

class A : public Ptrs<A>
{
public:
    using Ptrs<A>::uptr;
    using Ptrs<A>::sptr;
    using Ptrs<A>::wptr;
};

class B : public Ptrs<B>, public A
{
public:
    using Ptrs<B>::uptr;
    using Ptrs<B>::sptr;
    using Ptrs<B>::wptr;
};

此方案通过概念避免错误使用CRTP模板,同时让派生类自主控制别名的public可见性,解决继承冲突。

做法合理性分析

  • 优点:类内别名或全局模板别名都能减少重复代码,简化智能指针的书写,提升代码可读性。
  • 缺点:CRTP继承方式会增加类的继承层次,在复杂继承场景下容易出现别名不符合预期或二义性问题,提升了代码理解成本。
  • 建议:优先选择非侵入式全局模板别名,写法简洁且无继承副作用;若坚持使用类内别名形式,方案1的私有继承+using覆盖是更简单的实现方式。

内容的提问来源于stack exchange,提问作者jacky la mouette

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:40:38