有没有一种成语/设计模式用于限制模板?

3
如何将类型名称 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更加干净和详细。


2
搜索“C++概念”。我相信GCC已经有一个可以工作的实现。 - Angew is no longer proud of SO
1
可能是重复的问题 https://dev59.com/QXNA5IYBdhLWcg3wrPyq - Mansuro
1
@Angew 很遗憾,它在C++11/14中不可用。 - themagicalyang
@themagicalyang 还有Boost.Concepts。我并不是指C++上下文可以直接使用,而是指在这里你可以得到灵感。 - Angew is no longer proud of SO
4个回答

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

我并不认为 OP 的代码片段有什么优势。 - Jarod42
@Jarod42 这里还有更多的 C++ 内容哦 :P - 101010
@101010 真的。当需要检查多个类型时,我不确定它的可扩展性如何。在那种情况下,我可能会退回到静态断言。 - themagicalyang
@themagicalyang 如果有更多的类型需要检查,您也可以专门为这些类型进行检查。 - 101010
这个代码 template<typename T, typename... Args> Worker{} 可以运行吗? - themagicalyang

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

演示


问题在于它仍然说任何具有toString和get的内容都可以转到Worker,但实际情况并非如此。并非每种具有toString和get的类型都可行。因此,为了进行错误检查,所有可行的类型都可以从虚拟基类型(WorkableType)派生出来,仅用于执行静态断言(或启用if)。 - themagicalyang
@themagicalyang:你可能有更严格的特性(返回类型应该可转换/等于某种类型)。继承不是必需的。 - Jarod42

1
如果您确定要允许哪些类型,那么特性类是一种简洁的方法:
#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
};

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

当你明确地专门化时,这并没有真正帮助。 - themagicalyang
@themagicalyang 为什么不行呢?也许我误解了问题?如果是这样,我可以删除答案。请告诉我。 - skypjack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接