我的结构如下:
template <typename Arg1, typename Arg2>
class TemplateClass { ... };
template <typename TClass>
class UsesTemplateClass {
public:
UsesTemplateClass( TClass& instance ) inst{instance} { ... }
private:
TClass& inst;
};
template <typename TClass>
auto make_uses_template_class( TClass& instance ) {
return UsesTemplateClass<TClass>{ instance };
}
make_uses_template_class
存在的原因是函数模板可以推导类型,因此用户客户端不必显式指定它。我意识到 C++17 和 C++20 对于此类事情有更好的语法,但我需要使用 C++14。
只要传入
make_uses_template_class
的类型确实是 TemplateClass
的实例化,就可以正常工作,但如果不是,结果将是 UsesTemplateClass
内部出现一些可怕的错误。
我想做的是,如果
make_template_class
不是 TClass
,则确保不存在 TemplateClass
重载。另外,如果有人尝试使用不是 UsesTemplateClass
的参数手动实例化 TemplateClass
,我希望错误消息是合理的。
我知道有多种方法可以做到这一点,但我没有看到很多关于如何在这种情况下使用启用程序或 static_asserts 的一致指导。
例如,关于课程,我想我可以做这样的事情:
template <typename TClass>
class UsesTemplateClass; // declared but not defined
template <typename Arg1, typename Arg2>
class UsesTemplateClass<Arg1, Arg2> {
// real definition
};
这会起作用(如果你用
TemplateClass
以外的任何东西实例化它,它会抱怨 UsesTemplateClass<SomeOtherType>
不存在)。我对必须在我的专业化中显式指定 TemplateClass
的参数感到不高兴,因为在一般情况下,可能有几个模板参数可能会发生变化。
或者,我有一个想法,将
using template_class_tag = void
之类的东西放入 TemplateClass
中,然后将 UsesTemplateClass
定义为:
template <typename TClass,
typename = typename TClass::template_class_tag >
class UsesTemplateClass { ... };
但我在几个线程中看到,对类使用这种类型的启用器通常不受欢迎,并且通常建议使用
static_assert
。据我所知,普遍的共识是 static_assert
可以提供更好的错误消息,并且它不会像用户为默认模板参数指定类型那样被滥用。不幸的是,我不相信可以编写一个静态断言来判断 TClass::template_class_tag 类型是否存在。
为了解决这个问题,我想我可以给
TemplateClass
一个非模板库,并使用 std::is_base_of
的静态断言。我认为这会起作用,尽管它有点侵入性(基类没有其他目的)。
是否有一个普遍接受的习惯用法来以这种方式限制像
UsesTemplateClass
这样的类?
该函数也有同样的问题,但我知道启用器等在函数中的使用方式通常与在类中不同,所以我也想问一下。
我想做的是确保如果 TClass 不是 TemplateClass,则 make_template_class 重载不存在。另外,如果有人尝试使用不是 TemplateClass 的参数手动实例化 UsesTemplateClass,我希望错误消息是合理的。
一个简单的解决方案是将模板定义更改为:
template <typename T1, typename T2>
class UsesTemplateClass {
public:
UsesTemplateClass( TemplateClass<T1, T2>& instance ) inst{instance} {}
private:
TemplateClass<T1, T2>& inst;
};
template <typename T1, typename T2>
auto make_uses_template_class( TemplateClass<T1, T2>& instance ) {
return UsesTemplateClass<T1, T2>{ instance };
}
进行此更改后,以下内容将起作用:
auto a = make_uses_template_class(TemplateClass<int, double>());
auto b = UsesTemplateClass<int, double>(TemplateClass<int, double>());
但是以下会导致编译器错误:
auto a = make_uses_template_class(10);
auto b = UsesTemplateClass<int>(0);