2

Let's say I have a Server class, with a template argument. I need to ensure that the argument is a subclass of another Client class. Is it possible in C++?

For example, given something like this:

template <typename CLIENT>
class Server {
  void addClient(CLIENT client);
};

I'm expecting something like this:

template <typename CLIENT : Client>
class Server {
  void addClient(CLIENT client);
}

I want to have LoginServer and GameServer, both based on Server class, but each will work with different Client subclass.

class LoginServer : public Server<LoginClient>
class GameServer : public Server<GameClient>

I don't want to retype all Client types to LoginClient inside the LoginServer, otherwise compiler will throws error because of undefined methods etc. (LoginClient can have methods that Client hasn't, it's subclass).

Shoe
  • 74,840
  • 36
  • 166
  • 272
Krab
  • 6,526
  • 6
  • 41
  • 78
  • 1
    This seems like a case where you shouldn't be using templates at all, and just accepting a `const Client&` (so any object of a class derived from `Client` can be passed in by reference) – Brian Bi Mar 17 '14 at 22:37
  • To answer the general question, no. Unlike in Java, in C++ you cannot constrain the type of the class in a template. The logic being that if you are doing so, you're not actually making a truly templated class and can generally just use polymorphism. – aruisdante Mar 17 '14 at 22:42
  • Lots of ways to do it, e.g. just put a `static_cast(CLIENT*(0))` in the constructor. You'd probably have liked the Concepts proposed C++ which sadly didn't make it in to the C++11 Standard - they offered a dedicated mechanism for this, but at the cost of less readable compiler error messages and a few edge cases, this suffices. – Tony Delroy Mar 17 '14 at 22:42
  • possible duplicate of [Template Constraints C++](http://stackoverflow.com/questions/122316/template-constraints-c) – aruisdante Mar 17 '14 at 22:44
  • @Brian: lots of differences/benefits specific to templates - inlining, dead code elimination, static array sizing, loop unrolling etc., sometimes it's desirable to have distinct types instead of just Server. Of course some potential disadvantages too. – Tony Delroy Mar 17 '14 at 22:48
  • `static_assert(std::is_base_of::value, "...")` in the constructor maybe – Brian Bi Mar 17 '14 at 23:00
  • 3
    @krab: You say: 'I need to ensure that the client is subclass of Client class'. Why? Templates in C++ are resolved at compile time. If you do anything unsupported by the type you get a compile time error. Just assume CLIENT is a Client and treat it like a Client. If something goes wrong youll get a compile time error. – Muepe Mar 17 '14 at 23:00
  • @Muepe: hm yes, you are right. But it can be class with same interface or something like that. You know, i am confused why in Java this behavior is, if it looks like it is not necessary. – Krab Mar 17 '14 at 23:04
  • 1
    @Krab Java and C++ are different languages. And C++ templates are different from Java generics. It sounds like you don't actually need to enforce the requirement that the `CLIENT` template parameter type inherits from `Client`. So don't. :) In Java you might have needed to enforce that, but in C++ you most likely don't. – jalf Mar 17 '14 at 23:06
  • @Krab: Java is a bit a dull example as its a big mess when it comes to generics/templates. It tries some sort of C++s 'Do whatever you want' attitude with C#s (yea, I know it was after Java) 'You can only do what your constraints say' approach. Compare it to C#. In C# generic classes/methods are actually compiled to code. The constraints are therefore neccessary to make sure the end user (who faces compiled code) knows if he can feed a type to the class/method. In C++ no template code gets compiled unless it gets used. Every bit of code using a template has to be fully aware for the user – Muepe Mar 17 '14 at 23:13
  • of the template. Therefore the user can exactly say "Uhm, sorry, that wont work" as he has the full code and then compiles it with the type replaced by what he enters. – Muepe Mar 17 '14 at 23:13

1 Answers1

2

Let's say I have a Server class, with a template argument. I need to ensure that the argument is a subclass of another Client class. Is it possible in C++?

You can use type traits, specifically you can enable the class template via std::enable_if in conjunction with std::is_base_of:

template<class T, class Enable = void>
class Server;

template <typename CLIENT>
class Server<CLIENT, typename std::enable_if<std::is_base_of<Client, CLIENT>::value>::type> {};

So that give this hierarchy:

class Client {};
class Derived : public Client {};
class NonDerived {};

The following would compile:

Server<Derived> x;

but the following wouldn't:

Server<NonDerived> x;

Alternatively, as suggested in the comments, you could you static_assert within the class:

template <typename CLIENT>
class Server {
    static_assert(std::is_base_of<Client, CLIENT>::value, "CLIENT must be derived from Client");
    // ...
};
Shoe
  • 74,840
  • 36
  • 166
  • 272