In Java you can define generic class that accept only types that extends class of your choice, eg:
public class ObservableList {
...
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
:
#include
template
class observable_list {
static_assert(std::is_base_of::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
template
struct void_ {
using type = void;
};
template
using Void = typename void_::type;
template
struct has_const_iterator : std::false_type {};
template
struct has_const_iterator> : std::true_type {};
struct has_begin_end_impl {
template().begin()),
typename End = decltype(std::declval().end())>
static std::true_type test(int);
template
static std::false_type test(...);
};
template
struct has_begin_end : decltype(has_begin_end_impl::test(0)) {};
template
class observable_list {
static_assert(has_const_iterator::value, "Must have a const_iterator typedef");
static_assert(has_begin_end::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.