175

In Java you can define generic class that accept only types that extends class of your choice, eg:

public class ObservableList<T extends List> {
  ...
}

This is done using "extends" keyword.

Is there some simple equivalent to this keyword in C++?

mgamer
  • 12,296
  • 23
  • 84
  • 142
  • 1
    quite old question already...I feel what is missing here (also from the answers) is that Java generics arent really an equivalent of templates in C++. There are similarities, but imho one should be careful with directly translating a java solution to C++ just to realize that they are maybe made for different kinds of problems ;) – 463035818_is_not_a_number Aug 21 '18 at 11:48

14 Answers14

147

This typically is unwarranted in C++, as other answers here have noted. In C++ we tend to define generic types based on other constraints other than "inherits from this class". If you really wanted to do that, it's quite easy to do in C++11 and <type_traits>:

#include <type_traits>

template<typename T>
class observable_list {
    static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
    // code here..
};

This breaks a lot of the concepts that people expect in C++ though. It's better to use tricks like defining your own traits. For example, maybe observable_list wants to accept any type of container that has the typedefs const_iterator and a begin and end member function that returns const_iterator. If you restrict this to classes that inherit from list then a user who has their own type that doesn't inherit from list but provides these member functions and typedefs would be unable to use your observable_list.

There are two solutions to this issue, one of them is to not constrain anything and rely on duck typing. A big con to this solution is that it involves a massive amount of errors that can be hard for users to grok. Another solution is to define traits to constrain the type provided to meet the interface requirements. The big con for this solution is that involves extra writing which can be seen as annoying. However, the positive side is that you will be able to write your own error messages a la static_assert.

For completeness, the solution to the example above is given:

#include <type_traits>

template<typename...>
struct void_ {
    using type = void;
};

template<typename... Args>
using Void = typename void_<Args...>::type;

template<typename T, typename = void>
struct has_const_iterator : std::false_type {};

template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};

struct has_begin_end_impl {
    template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
                         typename End   = decltype(std::declval<const T&>().end())>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};

template<typename T>
class observable_list {
    static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
    static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
    // code here...
};

There are a lot of concepts shown in the example above that showcase C++11's features. Some search terms for the curious are variadic templates, SFINAE, expression SFINAE, and type traits.

Orwellophile
  • 11,307
  • 3
  • 59
  • 38
Rapptz
  • 19,461
  • 4
  • 67
  • 86
  • 3
    I never realized C++ templates use duck typing until today. Kind of bizarre! – Andy Jun 09 '15 at 13:36
  • 2
    Given the extensive policy constraints **C++** introduced to **C**, not sure why `template` is such an offending concept. Thanks for the tip. – bvj May 24 '17 at 05:42
  • 1
    If someone wonders what is `template`: https://en.cppreference.com/w/cpp/language/parameter_pack – zardosht Aug 23 '20 at 21:52
109

I suggest using Boost's static assert feature in concert with is_base_of from the Boost Type Traits library:

template<typename T>
class ObservableList {
    BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
    ...
};

In some other, simpler cases, you can simply forward-declare a global template, but only define (explicitly or partially specialise) it for the valid types:

template<typename T> class my_template;     // Declare, but don't define

// int is a valid type
template<> class my_template<int> {
    ...
};

// All pointer types are valid
template<typename T> class my_template<T*> {
    ...
};

// All other types are invalid, and will cause linker error messages.

[Minor EDIT 6/12/2013: Using a declared-but-not-defined template will result in linker, not compiler, error messages.]

Jarod42
  • 173,454
  • 13
  • 146
  • 250
j_random_hacker
  • 47,823
  • 9
  • 95
  • 154
  • 1
    Static asserts are nice as well. :) – macbirdie May 17 '09 at 10:46
  • I don't want Boost, I am on embedded platform/phone. Would `template<> my_template {` perform partial specialisation for all types derived from `myBaseType` too? – John Dec 11 '11 at 21:28
  • 5
    @John: I'm afraid that specialisation would only match `myBaseType` exactly. Before dismissing Boost, you should know that most of it is header-only template code -- so there's no memory or time cost at runtime for things you don't use. Also the particular things you'd be using here (`BOOST_STATIC_ASSERT()` and `is_base_of<>`) can be implemented using only *declarations* (i.e. no actual *definitions* of functions or variables) so they won't take any space or time either. – j_random_hacker Dec 11 '11 at 23:20
  • 53
    C++11 has come. Now we can use `static_assert(std::is_base_of::value, "T must extend list")`. – Siyuan Ren Sep 10 '13 at 02:55
  • I like this answer because the solution shows a way to pull it off if you don't have boost or don't wish to use boost. I work in embedded space using special GCC tool-chain from a vendor with no support for boost but supports templates. thumbs up! – Eric Jul 20 '14 at 17:47
  • I tried imitating crazy Java generics use: `class UnitType>` ... `class Unit>` and I couldn't get it to work. Fortunately, I realized such madness wouldn't be necessary with C++ templates :) – Andy Jun 09 '15 at 13:33
  • Thanks for the note about double parenthesis being necessary. I was having compiler errors trying to use BOOST_STATIC_ASSERT in combination with is_base_of until I found your answer (I was using single parenthesis). – jfritz42 Aug 12 '15 at 16:36
  • 2
    BTW, the reason the double parenthesis is necessary is that BOOST_STATIC_ASSERT is a macro and the extra parenthesis prevent the preprocessor from interpreting the comma within the is_base_of function arguments as a 2nd macro argument. – jfritz42 Aug 13 '15 at 19:29
  • How use template in second example? Full code please. – ilw Nov 16 '17 at 15:27
  • 1
    @Andreyua: I don't really understand what is missing. You could try declaring a variable `my_template x;` or `my_template y;` and verify that the compiler allows these, and then declare a variable `my_template z;` and verify that it doesn't. – j_random_hacker Nov 16 '17 at 16:20
64

The simple solution, which no one have mentioned yet, is to just ignore the problem. If I try to use an int as a template type in a function template that expects a container class such as vector or list, then I will get a compile error. Crude and simple, but it solves the problem. The compiler will try to use the type you specify, and if that fails, it generates a compile error.

The only problem with that is that the error messages you get are going to be tricky to read. It is nevertheless a very common way to do this. The standard library is full of function or class templates that expect certain behavior from the template type, and do nothing to check that the types used are valid.

If you want nicer error messages (or if you want to catch cases that wouldn't produce a compiler error, but still don't make sense) you can, depending on how complex you want to make it, use either Boost's static assert or the Boost concept_check library.

With an up-to-date compiler you have a built_in static_assert, which could be used instead.

jalf
  • 229,000
  • 47
  • 328
  • 537
  • 7
    Yes, I always thought that templates are the closest thing to duck typing in C++. If it has all the elements necessary for a template, it can be used in a template. –  May 18 '09 at 00:12
  • @John: I'm sorry, I can't make head or tails of that. Which type is `T`, and from where is this code called? Without some context, I have no chance of understanding that code snippet. But what I said is true. If you try to call `toString()` on a type which does not have a `toString` member function, then you'll get a compile error. – jalf Dec 11 '11 at 21:44
  • @John: next time, perhaps you should be a bit less trigger-happy downvoting people when the problem is in your code – jalf Dec 12 '11 at 08:12
  • @jalf, ok. +1. This was a great answer just trying to make it the best. Sorry for misreading. I thought we were talking about using the type as a parameter for classes not for function templates, which I suppose are members of the former but need invoking for the compiler to flag. – John Dec 12 '11 at 10:52
14

We can use std::is_base_of and std::enable_if:
(static_assert can be removed, the above classes can be custom-implemented or used from boost if we cannot reference type_traits)

#include <type_traits>
#include <list>

class Base {};
class Derived: public Base {};

#if 0   // wrapper
template <class T> class MyClass /* where T:Base */ {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
    typename std::enable_if<std::is_base_of<Base, T>::value, T>::type inner;
};
#elif 0 // base class
template <class T> class MyClass: /* where T:Base */
    protected std::enable_if<std::is_base_of<Base, T>::value, T>::type {
private:
    static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
};
#elif 1 // list-of
template <class T> class MyClass /* where T:list<Base> */ {
    static_assert(std::is_base_of<Base, typename T::value_type>::value , "T::value_type is not derived from Base");
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type base; 
    typedef typename std::enable_if<std::is_base_of<Base, typename T::value_type>::value, T>::type::value_type value_type;

};
#endif

int main() {
#if 0   // wrapper or base-class
    MyClass<Derived> derived;
    MyClass<Base> base;
//  error:
    MyClass<int> wrong;
#elif 1 // list-of
    MyClass<std::list<Derived>> derived;
    MyClass<std::list<Base>> base;
//  error:
    MyClass<std::list<int>> wrong;
#endif
//  all of the static_asserts if not commented out
//  or "error: no type named ‘type’ in ‘struct std::enable_if<false, ...>’ pointing to:
//  1. inner
//  2. MyClass
//  3. base + value_type
}
firda
  • 2,998
  • 14
  • 26
13

As far as I know this isn't currently possible in C++. However, there are plans to add a feature called "concepts" in the new C++0x standard that provide the functionality that you're looking for. This Wikipedia article about C++ Concepts will explain it in more detail.

I know this doesn't fix your immediate problem but there are some C++ compilers that have already started to add features from the new standard, so it might be possible to find a compiler that has already implemented the concepts feature.

jww
  • 83,594
  • 69
  • 338
  • 732
Barry Carr
  • 432
  • 4
  • 11
  • 4
    Concepts have been dropped from the standard unfortunately. – macbirdie Jul 24 '09 at 08:38
  • 4
    Constraints and concepts should be adopted for C++20. – Petr Javorik Jun 28 '18 at 17:50
  • It is possible even without concepts, using `static_assert` and SFINAE, as the other answers show. The remaining issue for somebody coming from Java or C#, or Haskell(...) is that the C++20 compiler does not do [definition checking](https://stackoverflow.com/questions/tagged/definition-checking) against the required concepts, which Java and C# do. – user7610 Dec 15 '19 at 19:55
11

An equivalent that only accepts types T derived from type List looks like

template<typename T, 
         typename std::enable_if<std::is_base_of<List, T>::value>::type* = nullptr>
class ObservableList
{
    // ...
};
nh_
  • 2,141
  • 12
  • 24
10

I think all prior answers have lost sight of the forest for the trees.

Java generics are not the same as templates; they use type erasure, which is a dynamic technique, rather than compile time polymorphism, which is static technique. It should be obvious why these two very different tactics do not gel well.

Rather than attempt to use a compile time construct to simulate a run time one, let's look at what extends actually does: according to Stack Overflow and Wikipedia, extends is used to indicate subclassing.

C++ also supports subclassing.

You also show a container class, which is using type erasure in the form of a generic, and extends to perform a type check. In C++, you have to do the type erasure machinery yourself, which is simple: make a pointer to the superclass.

Let's wrap it into a typedef, to make it easier to use, rather than make a whole class, et voila:

typedef std::list<superclass*> subclasses_of_superclass_only_list;

For example:

class Shape { };
class Triangle : public Shape { };

typedef std::list<Shape*> only_shapes_list;
only_shapes_list shapes;

shapes.push_back(new Triangle()); // Works, triangle is kind of shape
shapes.push_back(new int(30)); // Error, int's are not shapes

Now, it seems List is an interface, representing a sort of collection. An interface in C++ would merely be an abstract class, that is, a class that implements nothing but pure virtual methods. Using this method, you could easily implement your java example in C++, without any Concepts or template specializations. It would also perform as slow as Java style generics due to the virtual table look ups, but this can often be an acceptable loss.

Community
  • 1
  • 1
Alice
  • 3,822
  • 2
  • 22
  • 28
  • 3
    I'm not a fan of answers that use phrases such as "it should be obvious," or "everyone knows", and then go on to explain what is obvious or universally known. Obvious is relative to context, experience and context of experience. Such statements are inherently rude. – 3Dave Jul 08 '16 at 23:31
  • 3
    @DavidLively It's about two years too late to be criticizing this answer for etiquette, but I also disagree with you in this specific instance; I explained why they two techniques don't go together _before_ stating it was obvious, not after. I provided the context, and then said the conclusion from that context was obvious. That doesn't exactly fit your mold. – Alice Jul 10 '16 at 05:30
  • The author of this answer said something was obvious after doing some heavy lifting. I do not think the author intended to say the solution was obvious. – Luke Gehorsam Nov 13 '19 at 00:53
9

Executive summary: Don't do that.

j_random_hacker's answer tells you how to do this. However, I would also like to point out that you should not do this. The whole point of templates is that they can accept any compatible type, and Java style type constraints break that.

Java's type constraints are a bug not a feature. They are there because Java does type erasure on generics, so Java can't figure out how to call methods based on the value of type parameters alone.

C++ on the other hand has no such restriction. Template parameter types can be any type compatible with the operations they are used with. There doesn't have to be a common base class. This is similar to Python's "Duck Typing," but done at compile time.

A simple example showing the power of templates:

// Sum a vector of some type.
// Example:
// int total = sum({1,2,3,4,5});
template <typename T>
T sum(const vector<T>& vec) {
    T total = T();
    for (const T& x : vec) {
        total += x;
    }
    return total;
}

This sum function can sum a vector of any type that support the correct operations. It works with both primitives like int/long/float/double, and user defined numeric types that overload the += operator. Heck, you can even use this function to join strings, since they support +=.

No boxing/unboxing of primitives is necessary.

Note that it also constructs new instances of T using T(). This is trivial in C++ using implicit interfaces, but not really possible in Java with type constraints.

While C++ templates don't have explicit type constraints, they are still type safe, and will not compile with code that does not support the correct operations.

catphive
  • 3,451
  • 3
  • 29
  • 27
  • 2
    If you are suggesting never specializing templates can you also explain why it is in the language? –  Aug 07 '14 at 18:13
  • 1
    I get your point, but if your template argument must be derived from a specific type, then it's better to have an easy to interpret message from static_assert than the normal compiler error vomit. – jhoffman0x Aug 11 '14 at 20:05
  • 1
    Yes, C++ is more expressive here, but while that's generally a good thing (because we can express more with less), sometimes we want to *deliberately* limit the power we give ourselves, to gain certainty that we fully understand a system. – j_random_hacker Aug 13 '14 at 13:57
  • @Curg type specialising is useful when you want to be able to take advantage of some thing that can only be done for certain types. for example, a boolean is ~normally~ one byte each, even though one byte can ~normally~ hold 8 bits/booleans; a template collection class can (and in the case of std::map does) specialise for boolean so it can pack the data more tightly to conserve memory. – thecoshman Apr 01 '15 at 10:27
  • Also, to clarify, this answer is not say "never specialise templates" it's saying don't use that feature to try to limit what types can be used with a template. – thecoshman Apr 01 '15 at 10:28
  • If the type has no constraint, then how the IDE can show a list of callable methods of the object? Is the reason of this design that the folks that design C++ only use VIM instead of IDE? – jw_ Jan 05 '20 at 08:32
  • I'd go so far and argue that _because_ Java has type erasure, it doesn't actually care at runtime. The type restriction is there as a sanity check for the one using the API, to tell the programmer "hey, I'm expecting something that conforms to this sort of interface", so that the programmer can know at a glance what works without having to dig through the source or documentation. It's why we have static analysis at all: to catch human errors. – Electric Coffee Jan 14 '20 at 00:00
7

That's not possible in plain C++, but you can verify template parameters at compile-time through Concept Checking, e.g. using Boost's BCCL.

As of C++20, concepts are becoming an official feature of the language.

macbirdie
  • 15,585
  • 6
  • 44
  • 52
5
class Base
{
    struct FooSecurity{};
};

template<class Type>
class Foo
{
    typename Type::FooSecurity If_You_Are_Reading_This_You_Tried_To_Create_An_Instance_Of_Foo_For_An_Invalid_Type;
};

Make sure derived classes inherit the FooSecurity structure and the compiler will get upset in all the right places.

Stuart
  • 69
  • 1
  • 4
  • @Zehelvion `Type::FooSecurity` is used in template class. If the class, passed in template argument, hasn't `FooSecurity`, attempting to use it causes an error. It's sure that if class passed in template argument hasn't FooSecurity it isn't derived from `Base`. – GingerPlusPlus Aug 19 '14 at 15:45
4

C++20 concept usage

https://en.cppreference.com/w/cpp/language/constraints cppreference is giving the inheritance use case as an explicit concept example:

template <class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T);  // T is constrained by Derived<T, Base>

For multiple bases I'm guessing the syntax will be:

template <class T, class U, class V>
concept Derived = std::is_base_of<U, T>::value || std::is_base_of<V, T>::value;
 
template<Derived<Base1, Base2> T>
void f(T);

GCC 10 appears to have implemented it: https://gcc.gnu.org/gcc-10/changes.html and you can get it as a PPA on Ubuntu 20.04. https://godbolt.org/ My local GCC 10.1 did not recognize concept yet, so not sure what is going on.

1

There is no keyword for such type checks, but you can put some code in that will at least fail in an orderly fashion:

(1) If you want a function template to only accept parameters of a certain base class X, assign it to a X reference in your function. (2) If you want to accept functions but not primitives or vice versa, or you want to filter classes in other ways, call a (empty) template helper function within your function that's only defined for the classes you want to accept.

You can use (1) and (2) also in member functions of a class to force these type checks on the entire class.

You can probably put it into some smart Macro to ease your pain. :)

Hermes
  • 486
  • 5
  • 10
Jaap
  • 11
  • 1
1

Is there some simple equivalent to this keyword in C++?

No.

Depending on what you're trying to accomplish, there might be adequate (or even better) substitutes.

I've looked through some STL code (on linux, I think it's the one deriving from SGI's implementation). It has "concept assertions"; for instance, if you require a type which understands *x and ++x, the concept assertion would contain that code in a do-nothing function (or something similar). It does require some overhead, so it might be smart to put it in a macro whose definition depends on #ifdef debug.

If the subclass relationship is really what you want to know about, you could assert in the constructor that T instanceof list (except it's "spelled" differently in C++). That way, you can test your way out of the compiler not being able to check it for you.

Jonas Kölker
  • 7,184
  • 2
  • 38
  • 49
-2

Well, you could create your template reading something like this:

template<typename T>
class ObservableList {
  std::list<T> contained_data;
};

This will however make the restriction implicit, plus you can't just supply anything that looks like a list. There are other ways to restrict the container types used, for example by making use of specific iterator types that do not exist in all containers but again this is more an implicit than an explicit restriction.

To the best of my knowledge a construct that would mirror the statement Java statement to its full extent does not exist in current standard.

There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.

In C++11, the introduction of concepts should make this easier but I don't think it'll do exactly what you'd want either.

Rapptz
  • 19,461
  • 4
  • 67
  • 86
Timo Geusch
  • 23,267
  • 4
  • 48
  • 70