3

How do one restrict the typename T to specific type?
Consider this:

template <typename T>
struct Worker {
    // T can only be certain type allowing specific functionality.
    // i.e T needs to be a product of some interface, support some functions, say T::toString(),  T::print(), T::get().
    // Do something with T 
};

This is what I usually end up doing:

struct WorkableType { 
    std::string toString() { return ""; }
    int get() { return 0;}
}

struct WorkabelTypeA : WorkableType {
    std::string toString() { return "A"; }
    int get() { return 1;}
};

//Similarly
struct WorkableTypeB : WorkableType;

And use static assert and std::is_base_of:

template <typename T>
struct Worker {
    static_assert(std::is_base_of<WorkableType, T>::value, "Needs workable type");
    // Do something with T 
};

Is there any other design pattern, a more C++ way to restrict accidental instantiation of bad typed templates?

Edit: Seems like this would be better solved with C++ Concepts when it becomes the standard. Until then i guess, static_assert is probably more cleaner and verbose than enable_if.

themagicalyang
  • 2,493
  • 14
  • 21

4 Answers4

2

You could use SFINAE and template specialisation:

// type trait that evaluates always to false to use in the primary template
template<typename ... T> struct always_false : std::false_type { };

// primary template
template<typename T, typename Enable = void>
struct Worker {
  static_assert(always_false<T, Enable>::value, "Needs workable type");
};

// specialisation
template<typename T>
struct Worker<T, std::enable_if_t<std::is_base_of<WorkableType, T>::value>> {
...
};
Nawaz
  • 353,942
  • 115
  • 666
  • 851
101010
  • 41,839
  • 11
  • 94
  • 168
1

You can create traits and check that in your class, So, no need of inheritance. For example:

template <typename T>
using toString_t = decltype(std::declval<T>().toString());

template <typename T>
using get_t = decltype(std::declval<T>().get());

// Use C++17, but can be done in C++11
template <typename T>
using has_toString = std::is_detected<toString_t, T>;

template <typename T>
using has_get = std::is_detected<get_t, T>;

And then

template <typename T>
struct Worker {
    static_assert(has_toString<T>::value, "T should have toString");
    static_assert(has_get<T>::value, "T should have get");
};

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • The issue is that it still says anything that has toString and get can go to Worker, while that's not exactly the case. Not every type that has toString and get may be workable. So to do a error check, all types that are workable can be derived from a dummy base type (WorkableType) for the sole purpose of doing a static assert (or enable if). – themagicalyang Sep 05 '16 at 08:00
  • @themagicalyang: You may have more strict traits (as return type should be convertible/equal to some type). Inheritance is not needed. – Jarod42 Sep 05 '16 at 08:05
1

If you know exactly which types you want to allow, then a traits class is a succinct way to do it:

#include <utility>

// by default nothing is workable
  template<class T>
  struct is_workable : std::false_type
  {
  };

template <typename T>
struct Worker {
  static_assert(is_workable<T>(), "not a workable type");
    // T can only be certain type allowing specific functionality.
    // i.e T needs to be a product of some interface, support some functions, say T::toString(),  T::print(), T::get().
    // Do something with T 
};

// define a worker
struct A {};

// make it workable
template<> struct is_workable<A> : std::true_type {};

// define another worker but forget to make it workable
struct B {};

int main()
{
  Worker<A> wa{};
//  Worker<B> wb{};  // compile error - not workable
};
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

You can use specializations as it follows:

#include<string>

struct WorkableType { 
    std::string toString() { return ""; }
    int get() { return 0; }
};

struct A {};
struct B {};

template<typename> struct Worker;
template<> struct Worker<A>: WorkableType {};

int main() {
    Worker<A> wa;
    // this won't compile
    // Worker<B> wb;
}
skypjack
  • 49,335
  • 19
  • 95
  • 187