为什么无法重载类模板

Why is it not possible to overload class templates?

本文关键字:重载 为什么      更新时间:2023-10-16

阅读这个问题让我想知道:不允许类模板重载是否有技术原因?

通过重载,我的意思是有几个具有相同名称但不同参数的模板,例如

template <typename T>
struct Foo {};
template <typename T1, typename T2>
struct Foo {};
template <unsigned int N>
struct Foo {};

编译器设法处理重载的函数和函数模板,是否可以将相同的技术(例如名称重整)应用于类模板?

起初,我认为在单独使用模板标识符时可能会导致一些歧义问题,但唯一可能发生的情况是在将其作为模板模板参数传递时,因此可以使用参数的类型来选择适当的重载:

template <template <typename> class T>
void A {};
template <template <unsigned int> class T>
void B {};
A<Foo> a; // resolves to Foo<T>
B<Foo> b; // resolves to Foo<N>

你认为这样的功能有用吗?是否有一些"好"(即技术)原因说明这在当前C++中是不可能的?

模板

完整指南(亚马逊)中的第 12.5 节包含以下引用:

您可能合理地想知道为什么只有类模板可以部分专用化。原因大多是历史原因。 也许可以为函数模板定义相同的机制(参见第 13 章)。

在某些方面 重载函数模板的效果类似,但也存在一些细微的区别。这些差异是 主要与主模板需要 遇到使用时抬头查找。专业是 仅在之后考虑,以确定应采用哪种实现 使用。

相反,必须引入所有重载的函数模板 通过查找它们进入过载集,它们可能来自 不同的命名空间或类。这增加了 无意中使模板名称有些过载。

相反,它 也可以想象允许类模板的重载形式。 下面是一个示例:

// invalid overloading of class templates
template<typename T1, typename T2> class Pair; 
template<int N1, int N2> class Pair; 

但是,似乎并不迫切需要 这样的机制。

此外,C++(亚马逊)的设计与演变在第 15.10.3 节中包含此引用

因此,我的结论是,我们需要一个"专业化"的机制 模板。这可以通过接受一般重载来完成 或通过一些更具体的机制。我选择了特定的机制 因为我认为我主要是在解决由以下原因引起的违规行为 C 语言中的不规则性,并且因为过载的建议总是 引起抗议的嚎叫。我试图保持谨慎和 保守的;我现在认为这是一个错误。专业化为 最初定义的是一种受限和异常形式的重载 这与语言的其余部分不符。

大胆强调我的。我将其解释为函数重载解析比类专业化更难实现(并且由用户正确)。所以可能没有真正的技术障碍(类似于函数模板部分专业化),而是一个历史事故。

您不能"重载"类型参数、非类型参数和模板参数,但您可以专门化可变参数模板:

template <typename... T>
struct Foo;
template <typename T1>
struct Foo<T1> {};
template <typename T1, typename T2>
struct Foo<T1,T2> {};

这已经存在了一段时间,但我在搜索时仍然找到了这篇文章。感谢@log0为我提供了一个良好的开端。这是一个解决方案,可避免需要为所有可能的枚举提供模板专用化。它确实做了一个假设:您可以根据其自身及其基类来定义每个模板扩展。(这将在下面FooImpl中完成):

template <typename... T>
struct Foo;
template<typename T>
struct Foo<T> { /* implementation of base class goes here*/};
template <typename C, typename Base>
struct FooImpl : public Base { /* implementation of derived class goes here */};
template<typename C, typename... Bases>
struct Foo<C, Bases...> : FooImpl<C, Foo<Bases...> > { /*NO IMPLEMENTATION HERE */};

使用 FooImpl 打破了否则产生的模棱两可的递归。然后,这允许如下声明:

Foo<int> foo_int;
Foo<int, double> foo_int_double;
Foo<int, float, double> foo_int_float_double;

也许这就是STL现在的做法?