不使用模板参数的模板类

Template class with no use of template argument

本文关键字:参数      更新时间:2023-10-16

我在这样定义的类上遇到过很多次错误

class PureVirtualClass
{
   virtual int foo() = 0;
   virtual bool bar() = 0;
}
template <class T> class ImplClass : public virtual PureVirtualClass
{
   virtual ~ImplClass(){};
   int foo()  { return 42;}
   bool bar() { return true;}
   //several other method having nothing to do with T
}

这种"设计"经常出现,我想最初的开发人员知道他在做什么,将ImplClass定义为模板类,但没有对模板参数T的任何引用。我自己的c++模板知识有点有限。

这有好处吗?还是只是一个困惑的程序员?

对于被模板化但不依赖于参数的类来说,这是一个好处。大多数情况下,你会看到这样的东西来为模板元编程定义(空)标记结构:

template <class X>
struct some_tag {};

一般来说,像您这样的类的好处是,虽然您在每个类中都有相同的功能,但它们是不同的类,您不能将其中一个复制到另一个中,即ImplClass<int>类型的对象与ImplCalss<float>类型的另一个对象不兼容。

Arne提到的这个想法有很多有用的例子。例如,看看非常基本的元组实现,这就是单个元组元素的定义方式:

template <size_t N, typename T>
class TupleElem
{
    T elem;
public:
    T&       get()       { return elem; }
    const T& get() const { return elem; }
};

它是在N上模板化的,没有依赖它。为什么?因为元组实现

template <size_t... N, typename... T>
class TupleImpl <sizes <N...>, T...> : TupleElem <N, T>...
{
    //..
};

导出多个这样的元素,每个元素都有一个唯一的N,用作标识符。如果没有它,如果参数包T...中的两个元素类型相同,TupleImpl将两次派生同一类。在这种情况下,对元素的随机访问都不起作用(通过对适当的TupleElem基类的函数get()的显式调用,这将是不明确的),也不起空基优化作用(通过为空类型专门化TupleElem T而不具有类型T的数据成员)。

这是一个真实的用例,以及std::tuple是如何通过clang实现的。当然,像TupleElem这样的类将是一个隐藏的实现细节,而不是接口的一部分。例如,gcc遵循完全不同的递归类设计。

通常,您需要研究上下文,其中使用类来理解设计器的意图。

也许开发人员只是懒得将类拆分为.h和.cpp文件?

如果不使用模板,如果在多个编译单元中使用类,则会发生链接器错误。当使用模板时,链接器通常会在链接时丢弃模板的重复实例(或以不同的方式处理问题)。

虽然这可能是"开发人员为什么要这样做"的答案,但如果问题是"我应该在什么时候引入从未使用过的模板参数",我不建议这样做(请参阅其他答案)。尽管将代码拆分为.h和.cpp很烦人(尤其是用于Java或C#等语言时),但这是常见的C++方式。而且它肯定比仅为此目的使用模板更容易阅读/理解。此外,它还降低了类的使用的可读性。