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

Also wondered the same question, that's a C++20 approach through concepts.

#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
{};

Create a concept with name CheckType which is satisfied when the concept's parameter Type is a BaseClass or a DerivedClass : BaseClass.

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

Create a generic class where the T type REQUIRES to be MyClass or Derived : MyClass type.

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

We can use enable_if here if you have C++11 or higher available to you

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

For example:

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


Since I can't comment on the accepted answer, I'm providing a new answer which builds on it.

The template parameters can be simplified by having the enable_if condition become the default type template parameter instead of nullptr.

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

Technically, as the other answers show, there are ways to restrict it to subtypes of a certain type at compile time. However, most of the time, you would just do

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

without needing to specify a bound.

In Java, bounds are needed for generics because the generic class or method is compiled separately from any uses of it. Generic classes or methods are compiled once, into a single version in the bytecode, a single version that is able to handle any arguments that callers throw at it that satisfy the bounds in its declaration.

The compiler must type-check uses of the type T in the body of the method, like method calls, field accesses, etc., without knowing what T is, so you must provide a bound so the compiler can be satisfied that for example a method call is valid because it is defined on all types that satisfy that bound. For example, if you had the expression bar.baz() in the body of the method, the compiler will only let you compile if the type MyClass (and hence all subtypes of it) provides the method .baz(); if you had provided no bounds, the compiler would complain that Object (the implicit upper bound) has no method .baz().

C++ templates are different. The templated class or function is "instantiated" (compiled again) for every different type argument it is used for. So at the time of compiling the body of the function for a particular T, the compiler knows what T is, and is able to type-check uses of that type directly.

So if you had the expression bar.baz() in the body of the function, that would be fine. If you used this function with T being a type that that extends MyClass, then it will compile fine, because such a type has a .baz(). If you use this function with a type that doesn't have a .baz(), then it will fail to compile at that usage of it. If you accidentally use the function with a type that doesn't extend MyClass but has a .baz() whose parameter types and return type match the way you are using it, it will compile too; but that's not necessarily a bad thing. C++ templates are not usually used with type hierarchies, but rather with requirements on what the type needs to provide. So for example, a sorting algorithm is not going to require that its container and/or element type extend a certain type, but rather that the container provide certain features (e.g. random access subscript operator), and the element type provide certain features (e.g. a less-than operator).