C++ 相当于对 java parameter/return 类型使用 <T extends Class>

C++ equivalent of using <T extends Class> for a java parameter/return type

在 java 中,要创建一个 returns 与参数类型相同并扩展某个 class 的对象的函数,我将键入:

public <T extends MyClass> T foo(T bar) {...}

是否有与此等效的 C++?

换句话说,我如何制作一个函数,它接受任何 class 扩展某个 class 和相同类型的 returns? (这是为了 abstract/pure 虚拟 classes 的目的)。

如果您有可用的 C++11 或更高版本,我们可以在此处使用 enable_if

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
    return T();
}

例如:

class MyClass
{
public:
    int a = 1;
};

class Derived : public MyClass
{
public:
    int b = 2;
};

class NotDerived
{
public:
    int b = 3;
};

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
    return T();
}

int main()
{
    Derived d;
    NotDerived nd;
    std::cout << Foo(d).b << std::endl;; // works
    //std::cout << (Foo(nd)).b << std::endl;; //compiler error

    return 0;
}

Live Demo

由于我无法对已接受的答案发表评论,因此我提供了一个以此为基础的新答案。

可以通过将 enable_if 条件变为 default type template parameter 而不是 nullptr 来简化模板参数。

template<typename T, typename = std::enable_if<std::is_base_of<MyClass, T>::value>>

从技术上讲,正如其他答案所示,有一些方法可以在编译时将其限制为特定类型的子类型。然而,大多数时候,你会做

template <typename T> T foo(T bar) {...}

无需指定界限。

在 Java 中,泛型需要边界,因为泛型 class 或方法是与它的任何使用分开编译的。泛型 classes 或方法被编译一次,成为字节码中的单一版本,单一版本能够处理调用者抛给它的任何满足其声明范围的参数。

编译器必须在方法主体中对类型 T 的使用进行类型检查,例如方法调用、字段访问等,而不知道 T 是什么,因此您必须提供一个界限,这样编译器就可以满足,例如一个方法调用是有效的,因为它是在满足该界限的所有类型上定义的。例如,如果方法主体中有表达式 bar.baz(),编译器只会在类型 MyClass(以及它的所有子类型)提供方法 .baz();如果您没有提供边界,编译器会抱怨 Object(隐式上限)没有方法 .baz().

C++ 模板不同。模板化的 class 或函数是 "instantiated" (再次编译)用于每个不同类型的参数。因此,在为特定 T 编译函数体时,编译器知道 T 是什么,并且能够直接对该类型的使用进行类型检查。

所以如果你在函数体中有表达式 bar.baz(),那就没问题了。如果你使用此函数时 T 是一个扩展 MyClass 的类型,那么它将编译正常,因为这样的类型有一个 .baz()。如果您将此函数与没有 .baz() 的类型一起使用,那么它将无法在该用法下进行编译。如果你不小心使用了一个类型没有扩展 MyClass 但有一个 .baz() 的函数,其参数类型和 return 类型与你使用它的方式相匹配,它也会编译;但这不一定是坏事。 C++ 模板通常不与类型层次结构一起使用,而是与类型需要提供的要求一起使用。因此,例如,排序算法不会要求其容器 and/or 元素类型扩展某种类型,而是容器提供某些功能(例如随机访问下标运算符),并且元素类型提供某些功能(例如小于运算符)。

也想知道同样的问题,那是通过概念的C++20方法。

#include <iostream>
#include <utility>
#include <concepts>
using namespace std;

class MyClass
{
    public:
        inline virtual void print() const noexcept { cout << "Myclass" << endl; }  
};  


class Derived : public MyClass
{
    public:
        inline virtual void print() const noexcept override { cout << "Derived" << endl; }  
};  


class NotDerived
{};

创建一个名称为 CheckType 的概念并满足 当概念的参数 Type 是 BaseClass 或 DerivedClass : BaseClass.

template <class Type, class BaseClass>
concept CheckType = std::is_base_of<BaseClass, Type>::value;

创建一个泛型 class,其中 T 类型需要是 MyClass 或 Derived : MyClass 类型。

template <class T>
requires CheckType<T, MyClass>
class OnlyMyClass
{
    private:
        T instance;

    public:
        inline void print() const noexcept { instance.print(); }    // Print
};  


int main()
{
    //OnlyMyClass<NotDerived> o1; // error, the associated constraints are not satisfied.
    OnlyMyClass<MyClass>    o2; // nice!
    OnlyMyClass<Derived>    o3; // nice!
    o2.print();
    o3.print();
    
    return 0;
}   // main