是否有限制模板的习惯用法/设计模式?

Is there an idiom/design pattern for restricting templates?

本文关键字:惯用法 设计模式 习惯 有限制 是否      更新时间:2023-10-16

如何将类型名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++概念,当它成为标准。在此之前,我猜,static_assert可能比enable_if更干净,更详细。

您可以使用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>> {
...
};

你可以创建特征并在你的类中检查,所以,不需要继承。例如:

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");
};
演示

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

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

可以这样使用专门化:

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