In java, to make a function that returns an object that is the same type as a parameter and extends a certain class, I would type:
public <T extends MyClass> T foo(T bar) {...}
Is there a C++ equivalent of this?
In other words, how do I make a function that takes any class that extends a certain class, and returns that same type? (This is for the purpose of abstract/pure virtual classes).
< T > is a conventional letter that stands for "Type", and it refers to the concept of Generics in Java. You can use any letter, but you'll see that 'T' is widely preferred. WHAT DOES GENERIC MEAN? Generic is a way to parameterize a class, method, or interface.
What Does Class Mean? A class — in the context of Java — is a template used to create objects and to define object data types and methods.
So methods of generic or nongeneric classes can use generic types as argument and return types as well. Here are examples of those usages: // Not generic methods class GenericClass < T > { // method using generic class parameter type public void T cache ( T entry ) { ... } }
To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number .
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With