我是否应该定义一个接口,该接口明确告知用户他/她应该实现什么以便将类用作模板参数,或者让编译器在未实现功能时警告他?
template <Class C1, Class C2>
SomeClass
{
...
}
C1类必须实现某些方法和运算符,编译器在使用之前不会发出警告。我应该依靠编译器来警告或确保我这样做:
Class C1 : public SomeInterfaceEnforcedFunctions
{
// Class C1 has to implement them either way
// but this is explicit? am I right or being
// redundant ?
}
答案 0 :(得分:3)
理想情况下,您应该使用concept
来指定用作模板参数的类型的要求。不幸的是,目前的和即将推出的标准都不包括concept
s。
如果没有,有多种方法可用于执行此类要求。您可能希望阅读Eric Neibler's article有关如何强制执行模板参数的要求。
我同意Eric的断言,即将所有内容留给编译器通常是不可接受的。这是我们大多数人与模板相关的可怕错误消息的大部分来源,其中看似琐碎的拼写错误会导致页面难以辨认。
答案 1 :(得分:1)
如果您要强制使用界面,那么为什么要使用模板呢?你可以简单地做 -
class SomeInterface //make this an interface by having pure virtual functions
{
public:
RType SomeFunction(Param1 p1, Param2 p2) = 0;
/*You don't have to know how this method is implemented,
but now you can guarantee that whoever wants to create a type
that is SomeInterface will have to implement SomeFunction in
their derived class.
*/
};
接着是
template <class C2>
class SomeClass
{
//use SomeInterface here directly.
};
更新 -
这种方法的一个基本问题是它只适用于用户推出的类型。如果存在符合您的接口规范的标准库类型,或者具有符合SomeInterface的类的第三方代码或其他库(如boost),除非将它们包装在您自己的类中,否则它们将无法工作,实现接口并适当转发呼叫。我不知何故不再喜欢我的回答了。
答案 2 :(得分:1)
缺少 概念 ,用于描述模板参数必须满足哪些要求的现在放弃的概念(双关语无意,但注意到) 要求仅隐式强制执行 。也就是说,如果用户作为模板参数使用的任何内容都不能满足它们,则代码将无法编译。不幸的是,由此产生的错误消息通常是相当愚蠢的。你可以做的唯一改善问题的方法是
static_assert
拯救!)甚至是不可能的,这就是被认为成为核心语言特征而不是库的概念。 请注意,以这种方式 忽略一项要求 很容易,只有当某人使用某种类型作为模板参数时才会显而易见。但是,至少很容易忽视 要求通常会丢失 ,并且在说明中放入的内容比代码实际要求的要多得多。
例如,+
不仅定义为数字,还定义std::string
和任意数量的用户定义类型。因此,模板add<T>
不仅可以与数字一起使用,还可以与字符串和无限数量的用户定义类型一起使用。这是您想要抑制的代码的不必要的副作用还是您想要支持的功能取决于您。我所说的只是抓住这个并不容易。
我不认为用虚函数定义一个抽象基类形式的接口是个好主意。这是运行时多态,主要支柱经典OO。如果你这样做,那么你不需要一个模板,只需每个引用获取基类
但是你也失去了模板的一个主要优点,那就是它们在某些方面更灵活(尝试编写一个add()
函数经典OO,它适用于任何类型的重载+
并且更快,因为函数调用的绑定不是在运行时发生,而是在编译期间发生。 (由于内联能力的原因,这可能会带来比最初看起来更多的情况,这通常是运行时多态性无法实现的。)