是否存在限制模板的习语/设计模式?

时间:2016-09-05 07:10:30

标签: c++ templates design-patterns

如何将类型名称T限制为特定类型?
考虑一下:

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 
};

这就是我通常最终要做的事情:

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;

并使用静态断言和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 
};

是否有其他设计模式,更多C ++方式限制意外实例化错误的类型模板?

编辑:当C ++ Concepts成为标准时,似乎可以更好地解决这个问题。在那之前我想,static_assert可能比enable_if更清晰,更冗长。

4 个答案:

答案 0 :(得分:2)

您可以使用SFINAE和模板专业化:

// 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>> {
...
};

答案 1 :(得分:1)

您可以在类中创建特征并检查它,因此,不需要继承。例如:

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>;

然后

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

答案 2 :(得分:1)

如果你确切地知道你想要允许哪些类型,那么traits类就是一种简洁的方法:

#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
};

答案 3 :(得分:0)

您可以使用以下专业化:

#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;
}