首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

如何将模板类型限制为基类,而不是基类的子类?

将模板类型限制为基类而不是基类的子类,可以使用模板元编程技术中的类型萃取(type traits)来实现。

类型萃取是一种在编译期间获取类型信息的技术,它可以通过模板特化和SFINAE(Substitution Failure Is Not An Error)原则来判断一个类型是否是另一个类型的子类。

以下是一种实现方式:

代码语言:txt
复制
#include <type_traits>

template <typename Base, typename Derived>
struct is_base_of {
  private:
    template <typename T>
    static std::true_type test(const Base*);
  
    template <typename T>
    static std::false_type test(...);
  
  public:
    static constexpr bool value = decltype(test<Derived>(nullptr))::value;
};

template <typename Base, typename Derived>
constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;

使用上述代码,可以通过 is_base_of_v<Base, Derived> 来判断 Derived 是否是 Base 的子类。如果返回值为 true,则说明 DerivedBase 的子类,可以进行模板实例化;如果返回值为 false,则说明 Derived 不是 Base 的子类,编译器会报错。

以下是一个示例:

代码语言:txt
复制
#include <iostream>

class Base {
  public:
    virtual void foo() {
        std::cout << "Base::foo()" << std::endl;
    }
};

class Derived : public Base {
  public:
    void foo() override {
        std::cout << "Derived::foo()" << std::endl;
    }
};

template <typename T, typename = std::enable_if_t<is_base_of_v<Base, T>>>
void bar(T& obj) {
    obj.foo();
}

int main() {
    Base base;
    Derived derived;

    bar(base);    // 输出:Base::foo()
    bar(derived); // 输出:Derived::foo()

    return 0;
}

在上述示例中,bar 函数使用了类型萃取技术,将模板类型限制为 Base 的子类。当传入的对象是 Base 或其子类时,bar 函数可以正常调用;当传入的对象不是 Base 或其子类时,编译器会报错。

这种方式可以在编译期间进行类型检查,避免了在运行时出现类型错误的情况。同时,它也提供了更好的代码可读性和可维护性。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

Python中的协议 、鸭子类型 、 抽象基类 、混入类

本篇文章探讨一下python中的几个概念:协议 、鸭子类型 、 抽象基类 、混入类。 一、协议 在python中,协议是一个或一组方法。...二、鸭子类型(duck typing) 多态的一种形式,在这种形式中,对象的类型无关紧要,只要实现了特定的协议即可。...三、抽象基类 抽象基类就是定义各种方法而不做具体实现的类,任何继承自抽象基类的类必须实现这些方法,否则无法实例化。 那么抽象基类这样实现的目的是什么呢? 假设我们在写一个关于动物的代码。...注意,自己定义的抽象基类要继承 abc.ABC(abc.ABC 是 Python 3.4 新增的类,python2的语法不是这样的)。...print(issubclass(Cat, Animal)) 输出: True 这种通过注册和抽象基类关联起来的类叫做虚拟子类,虚拟子类不会继承注册的抽象基类,而且任何时候都不会检查它是否符合抽象基类的接口

1.9K20
  • 绑定子类的泛型基类,反模式?

    今天要说的主题正是基于LayerSupertype,并结合了泛型技术而实现的,同样,它还有一个重要的约定:泛型的类型参数必须是最终的子类。...,提供了统一的实体模板、约定和一些通用的基础实现。...基于这个基类的代码重用,使得子类的代码非常简单。这里和普通继承、普通泛型的不同点在于父类在运行时绑定了具体子类的类型。 设计原理     为什么要这样设计?基类为什么不直接使用非泛型的基类呢?...这是为了在基类实现的通用方法中,能够以强类型的方式直接访问最终的子类。...这是因为Article已经“告诉”基类EntityBase绑定子类的类型是Article,而不是GoodArticle,这按照EntityBase设计时的约定“T必须是最终的子类”相矛盾!

    1K50

    穿越Java世界的继承奇旅:从基类到子类的华丽蜕变

    ⑤提供模板和抽象基类:通过继承,我们可以创建模板类(也称为抽象基类或接口类),这些类只定义接口(即方法签名),而不实现它们。这样,子类就必须实现这些接口,从而强制实现特定的行为。...二:什么是继承以及继承的方式 继承是面向对象编程(OOP)中的一个核心概念,它提供了一种机制,允许我们基于已有的类来创建新的类,新创建的类被称为子类(或派生类),而原有的类被称为父类(或基类、超类)。...Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的 子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可...// 与父类中成员变量同名且类型相同 char b; // 与父类中成员变量同名但类型不同 // 与父类中methodA()构成重载 public void methodA(int a) {...2.继承不是万能的,过度使用继承会导致代码结构复杂、难以维护。在设计类时,应优先考虑组合(composition)而不是继承(inheritance)。

    8510

    一个简单的方法:截取子类名称中不包含基类后缀的部分

    基类是 MenuItem,子类是 WalterlvMenuItem、FooMenuItem。...在代码中,我们可能会为了能够一眼看清类之间的继承(从属)关系而在子类名称后缀中带上基类的名称。但是由于这种情况下的基类不参与实际的业务,所以对外(文件/网络)的名称通常不需要带上这个后缀。...本文提供一个简单的方法,让子类中基类的后缀删掉,只取得前面的那部分。 在这段代码中,我们至少需要获得两个传入的参数,一个是基类的名称,一个是子类的名称。...另外,我们还需要有一些约束,必须有一个类型是另外一个类型的子类。于是我们可能必须来使用泛型做这样的约束。.../// internal static class ClassNameUtils { /// /// 当某个类型的派生类都以基类

    23230

    简易理解设计模式之:模板方法模式——Android中的BaseActivity基类

    介绍: 模板方法模式属于行为型模式。定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。...类图: AbstractClass(抽象模板类):定义了一套算法框架。 ConcreteClass(具体实现类):实现模板方法步骤中未执行的方法。...用法: • 多个子类有公共的方法,并且逻辑基本相同时。 • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。...• 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽到父类中,然后通过子类约束其行为。...可能很多朋友已经在无意之中用到了这种模式,下面就让我们在Andoird上试一下: 需求:实现界面控制器的基类 1、继承实现 1.1、新建一个BaseActivity基类 public abstract

    68420

    《Effective Modren C++》 进阶学习(上)

    「小结」 如果表达式是一个变量名,则decltype推导出来的类型就是该变量的类型,而不是该变量的值的类型。...使用override声明重写函数 C++中子类可以重写基类的虚函数,但两者必须完全相同,才会被编译器认定为是重写的函数; 否则会被认定为子类自身的函数成员,且编译器不会提醒。...quite() {} // a.不符预期, 编译器不报错 int quite() override { } // b.不符预期, 编译器报错 }; 如上,预期设计是子类重写基类的...「小结」 override可以明确此函数是重写的基类虚函数接口,当基类不存在此接口时就会编译报错。...可以规避在声明子类接口时没有和基类保持一致,又难以察觉,导致子类接口在运行中没有被调用到这种低级问题。 13.

    20320

    C++ 继承:代码传承的魔法棒,开启奇幻编程之旅

    继承基类的成员,访问方式都被限制为protected;通过private继承基类的成员,访问方式都被限制为了private。...因为 per3 是 person 类型,而 stu 是 student 类型,当 stu 被复制给 per3 时,student 类特有的成员 _id 会被“切掉”,per3 将不会包含 _id 成员。...,后继承的基类在后面,派生类的成员在最后一个 菱形继承:是一种特殊的多继承,子类继承了多个父类,而这些父类又继承了同一个基类的数据和方法。...黑箱,对象的内部细节是不可见的。 组合的耦合度低 优先使用组合,而不是继承。...黑箱,对象的内部细节是不可见的。 组合的耦合度低 优先使用组合,而不是继承。

    10710

    《Effective C++》学习笔记

    对于一些可能在被别的类直接调用其成员函数、值的类,最好改为暴露一个返回其类对象的引用的函数的形式,而不是暴露其类对象本身,这可以保证在函数内完成初始化,避免被调用时还没有初始化。...,即使不是,也可能不符合你想要的目的(是父类的结果不是子类的结果)。...Handle classes是一个声明类,一个imp实现类,声明类中不涉及具体的定义,只有接口声明,在定义类中include声明类,而不是继承。...而Interface classes是在接口类中提供纯虚函数,作为一个抽象基类,定义类作为其子类来实现具体的定义。...对于嵌套从属类型名称(即依赖于模板参数类型的一个子类型,例如迭代器),必须用typename来修饰,但不能在模板类的基类列和初始化列表中修饰基类。

    1.1K20

    面试官问我多态,我是这么回答的

    两者的分类依据为多态的决定时机,静态多态由编译期决定,而动态多态由运行期决定。 静态多态 静态多态又分为函数重载和函数模板两种类型。...当一个基类指针或引用指向一个派生类对象时,便可以通过这个基类指针调用派生类中重写的函数,实现在运行时的多态。由此可知,动态多态需要有三要素: 1. 继承:要有基类和子类,甚至是多个子类 2....虚函数:基类内应有虚函数,子类最好要重写(override)虚函数 3. 指针或引用:指向子类对象的基类指针或引用 动态多态可以简单的认为是继承+虚函数实现。...虚函数表为类内所有虚函数的函数指针所组成的表,所以当子类重写虚函数时,子类的虚函数表内含有的虚函数指针为重写后的函数指针,也即子类和父类的虚函数表并不是同一个。...含有纯虚函数的类为虚基类,虚基类不能用于声明对象 禁止重写虚方法 从父类继承的虚方法默认为虚函数,当不希望该虚方法被子类重写时,可以使用final关键字注明,禁止该虚方法被重写。

    6510

    如何把CPP源程序改写成C语言?

    使用的时候在创建结构体变量的时候要用malloc而不是new,并且这个时候要手工调用初始化函数。...实例化类时作参数。 这三种情况下都是由系统直接调用类的拷贝构造函数而不是构造函数。 注意:C=D;不会调用拷贝构造函数,这种情况下使用的是重载‘=’运算符的方法。...除了将基类的构造函数名改为子类构造函数名外,不可以将基类定义的部分作其他改动。并在构造函数里调用基类的构造函数,然后如果子类覆盖了基类的函数,则要把该函数指针重定向到子类函数。...这是为了保持类的继承带来的动态联编的特性。 类之间的继承关系是复杂且多变的,为了保证基类在所有子类中的唯一而且方便修改,最好的方法就是把基类的结构体部分做成宏,在子类中直接使用即可。...多继承也是可以改的,将多个基类的成员全部拷到子类里,遇到重复的成员名,则在前面加上前缀来区别,当然这个指的是基类之间有相同的,如果是派生类和基类之间有重名的,则会覆盖基类。

    2.3K20

    十一、多态

    多态问题的引入背景 在面向对象编程中,多态的引入主要是为了解决以下问题: 提高代码的复用性:通过继承,子类可以继承父类的属性和方法,而多态则允许子类重写父类的方法,从而实现特定于子类的行为。...当通过基类指针删除派生类对象时,如果基类的析构函数不是虚的,那么只会调用基类的析构函数,而不会调用派生类的析构函数。...模板方法模式:在模板方法模式中,抽象类定义了一个算法的骨架,将一些步骤延迟到子类中实现。纯虚函数用于定义这些必须由子类实现的步骤。...总结 纯虚函数和抽象类是面向对象编程中用于实现接口和多态性的重要工具。纯虚函数要求派生类必须提供实现,而抽象类则是因为包含至少一个纯虚函数而不能被直接实例化。...模板:虽然模板本身并不直接支持多态(静态多态除外),但可以通过模板来编写与类型无关的代码,并在编译时根据具体的类型参数来生成相应的代码。这在一定程度上也体现了多态的思想。

    10110

    必知必会之C++多态机制

    : 模板是一种通用编程技术,允许编写与特定类型无关的代码。...通过使用模板,可以在不同类型的参数上执行相同的操作,而无需为每种类型编写不同的函数。...方法调用 在 C++ 中,如果父类通过指针或引用调用一个虚函数,而这个虚函数在子类中被重写(override),那么调用的实际方法将取决于指针或引用所指向的对象的类型。这就是多态的体现。...具体来说,如果父类指针或引用指向的是子类对象,那么调用的方法将是子类中重写的版本;如果指针或引用指向的是父类对象,那么调用的方法将是父类中的版本。...最后,通过 basePtr->show() 调用 show() 函数,由于 show() 是虚函数,因此调用的是 Derived 类中的版本,而不是 Base 类中的版本。

    16710

    从入门到精通:如何解决C++模板代码膨胀问题?

    将模板函数的通用部分提取出来 如果模板函数中有一部分代码与模板参数无关,那么可以将这部分代码提取出来,放到一个非模板函数中。这样,这部分代码只需要生成一次,而不是在每个模板实例中都生成一次。...将模板类的通用部分提取到基类 ❝特别注意:这里的基类指「非模板基类」,或者「模板参数比子类少的基类」;否则只是换个地方写模板类,起不到瘦身效果。...)子类的共用部分,挪到(少模板参数的)基类 如果基类也有模板参数,那么应尽量使基类的模板参数比子类少,并把子类的共用部分挪到基类。...结合前面 4.1 说的,基类压根就没用上这两个模板参数,进一步加剧了生成类型的数量。...为了减少模板实例化的大小,我们可以将 Shape 和 Color 类型的处理逻辑分离出来,使它们成为 GraphicObject 的成员,而不是模板参数。

    81210

    cc++问题集四

    ,如果基类都没有虚函数,就是特属子类的虚函数指针 image.png image.png image.png 2、c++泛型编程 泛型在C++中的主要实现为模板函数和模板类。...“Person1”的成员 system("pause"); return 0; } 类模板与继承 子类父类都要申明为模板类,子类继承父类的时要指父类的泛型 template class...多态中: 用于基类和子类之间的指针或引用的转换。把子类的指针或引用转换为基类表示时(向上转换)是安全的;但把基类的指针或引用转换为用子类表示时(向下转换),由于没有进行动态类型检测,所以是不安全的。...虚继承一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针vbptr(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(多重虚继承还是单一虚继承,指向虚基的指针都只有一个...);当虚继承的子类被当做父类继承时,虚基类指针也会被继承,如果是多重继承那就会有多个虚基指针。

    77740

    【C++】详细讲解继承(上)

    我们前面接触到的都是 函数 层次的复用,遇到过的 类 层次的复用有模板,而继承是 类层次 的一种新的复用。...Student st;//子类对象 Same sa; //父类对象 sa = st;//子类赋值给父类(可以,做切片) st = sa;//父类赋值给子类(不可以) 子类的 指针或者引⽤可以 通过强制类型转换赋值给父类的指针或者引...因为函数构成重载的要求是两个函数在同一作用域。而父类和子类有独立的作用域。...2.对自定义类型->调用默认构造 3.继承父类成员看作一个整体对象,要求调用父类的默认构造 派⽣类的构造函数必须调⽤基类的构造函数初始化基类的那⼀部分成员。...而调用析构次数太多会出问题。

    4000

    C++多态特性

    在派生类中实现的函数可以覆盖基类中的同名函数,而且会在运行时的对象类型上调用合适的函数。通过将基类指针或引用指向派生类对象,可以实现动态多态性。 (2)模板(template)。...(前面已经讲过了) 模板是一种通用的代码库,可以为不同的类型提供相同的代码实现。使用模板可以实现静态多态性。在编译期间,编译器会依据模板中使用的类型,生成适当的代码。...在父类中通过关键字virtual声明的函数为虚函数,子类可以覆盖并重新实现(重写)该函数。当通过父类的指针或引用调用虚函数时,实际调用的是子类中的实现,而不是父类的实现。这样就实现了多态....虚函数的特殊情况: 斜变 派生类重写基类虚函数时,与基类虚函数返回值类型不同。 基类虚函数返回基类对象的指针或者引用. 派生类虚函数返回派生类对象的指针或者引用时....虚函数重写需要遵守以下条件: 函数名称、参数列表和返回类型在父类和子类中必须完全相同。(三同) 函数在父类中必须被声明为virtual关键字,否则在子类中重写将不会产生多态效果。

    14370

    2023 跟我一起学设计模式:模板方法模式

    我们可为图中的三个解析算法创建一个基类, 该类将定义调用了一系列不同文档处理步骤的模板方法。 模板方法将算法分解为步骤, 并允许子类重写这些步骤, 而非重写实际的模板方法。...首先, 我们将所有步骤声明为 抽象类型, 强制要求子类自行实现这些方法。 在我们的例子中, 子类中已有所有必要的实现, 因此我们只需调整这些方法的签名, 使之与超类的方法匹配即可。...模板方法模式适合应用场景 当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。...从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同。 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。...Go 模板方法模式讲解和代码示例 模版方法是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

    14140

    深入理解面向对象编程特性 : 继承

    类模板的继承 类模板继承的基本语法 template class Base { // 基类内容 }; template class Derived : public...但是由于foo()是依赖于模板参数T的成员函数,编译器无法确定foo()是从基类继承的。这是因为模板是按需实例化的,编译器在第一次查找时并不知道派生类实例化时会包含哪些基类成员。...父类的指针或引用可以通过强制类型转换赋值给子类的指针或引用,但必须确保父类的指针实际上指向一个子类对象。...继承与静态成员 在C++中,静态成员是属于类而不是某个特定对象的。⽗类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员,这意味着即使类派生出了多个子类,它们都共享同一个静态成员实例。...注意事项 构造函数调用顺序:因为虚继承之后只存在一个实例,所以当使用虚继承时,基类的构造函数在最派生类(如Assistant)的构造函数中被调用,而不是在虚继承的直接派生类(如Student或Teacher

    15610

    C++之面向对象的语法笔记

    先调用父类的构造函数 //2.释放时先调用子类的析构函数 //子类没有 就使用父类的方法 //子类有实现,就是用子类的重写 //父类型的引用 赋值子类型的对象 方法都是父类型中的方法 void funExtends...输出结果.png 输出的结果,调用的还是父类的方法,不是子类的方法,上面的情况明显不是我们想要的结果,怎么解决呢?...当使用不同类型的继承时,遵循以下几个规则: 继承类型 说明 public 当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问...如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。...模板类.png 类型转换 C++ 常见的几种类型转换 static_cast 普通值类型转换 const_cast 去常量 dynamic_cast 基类和派生类之间的转换 reinterpret_cast

    1.6K40
    领券