为什么不能在完全专业化中引入新的模板参数?

Why can't new template parameters be introduced in full specializations?

本文关键字:参数 不能 专业化 为什么      更新时间:2023-10-16
在 C++11 标准中禁止 'template class A {...};'(如果有的话(?,已确认以下语法在 C++11 标准中是不允许的:

/* invalid C++ */
template <typename T>
class A
{
    public:
    T t;
};
// phony "full specialization" that mistakenly attempts
// to introduce a *new* template parameter
template <typename T>
class A<int>
{
    public:
    int i;
    T t;
};

在完全理解上述语法不代表有效的C++之后,我仍然可以想象在语法上明确使用上述代码片段,如下所示:

A<float> a1;
A<int><double> a2;
a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;

对于C++来说,支持上述语法在语法和语义上似乎是明确的。

(如果任何人都不清楚预期的语义,请发表评论,我会解释。

我将这种语法描述为"在完全专业化中引入新的模板参数"。

在这个设想的场景中,一个C++编译器被修改为支持上述语法和语义,编译器将看到A<int> a2;并识别尝试的实例化与主模板匹配;然后它将搜索专用化并查找并选择完整的专用化A<int>(忽略<double>(;然后它会注意到这种完全专用化引入了一个新的模板参数T在声明变量的情况下a2double

如果我是对的,上面的语法和语义是明确的,那么我想知道为什么这不是C++的一部分。 我可以想到三个原因,但也许答案是不同的或更复杂的。

  • (几乎(没有现实世界的场景会很有用
  • 对于当前的编译器来说,它太复杂了,无法要求支持
  • 与其他语言功能有太多潜在的冲突,需要许多新规则
  • 实际上,第四种可能性 - 上述语法和语义在某些实际场景中很有用,并且可以合理地包含在标准中,但它不是当前标准的一部分。

我想知道为什么C++不支持此功能 - 我的一个要点提供了答案是否正确?

,这以有趣的方式爆炸。

为了清楚起见,我假设你的意思是你的示例代码,

 A<double> a; // okay
 A<int> a2;   // not okay, A<int> is not a class but a class template.

现在让我们尝试在其他模板中使用它。考虑

template<typename T>
void function<A<T> const &a) { ... }
A<double> a;
A<int><double> a2;
function(a);  // that looks okay.
function(a2); // er...compiler error, I guess?

这还不算太糟糕;编译器可能会抱怨这一点。不过,我认为我们开始看到这有点奇怪。好吧,把它提升一个档次:

template<template<typename> class A> struct Foo { ... };
Foo<A> f; // this would usually work, does it now?

如果你的回答是否定的,如果在编译时不知道专业化怎么办?

编辑:稍微扩展一下,考虑模板模板参数的真实场景,我喜欢称之为包特征:

template<template<typename> class, typename...> struct applies_to_all;
template<template<typename> class predicate, typename T, typename... Pack>
struct applies_to_all<predicate, T, Pack...> {
  static bool const value =
    predicate<T>::value && applies_to_all<predicate, Pack...>::value;
};
template<template<typename> class predicate>
struct applies_to_all<predicate> {
  static bool const value = true;
};
...
bool b = applies_to_all<std::is_integral, int, long, double>::value;

并输入类似的东西A这是一个除int之外的所有内容的类和一个用于int的类模板.然后尝试锻炼

applies_to_all<A, double, float, std::string, int>::value

请注意,这个简单的案例不是您可能会看到的代码。你真正得到的是这些东西嵌套在其他模板中的三个层次,它看起来像这样:

applies_to_all<A, T...>::value

可能可以为此定义一个在数学上一致的行为,但我怀疑是否可以定义一个有用的行为。当然,它不符合 POLA 标准。

沿着这些思路,您可能想出更多。以这种方式模糊类和类模板之间的界限势必会破坏各种东西。

> C++ 中的名称表示一种实体,这种实体通过名字查找来识别。

示例中A的名称是类模板名称。因此,任何TA<T>都是类名。

对于语言来说,这是一个有用的属性。破坏此属性意味着语言的复杂性大幅增加(对于编译器和用户(。

因此,即使你能找到所有的边缘情况并修复Wintermute指出的问题,它仍然很可能被拒绝,因为它使语言更加不规则,更令人惊讶,不那么统一。C++已经背负着相当多的不规则行为;添加更多是没有意义的。

我鼓励您发布您的预期用例,我们可能会向您展示更好的方法。

模板分辨率始终针对主模板。

在专业化中引入的任何其他模板参数都将是凭空悬挂的,没有任何内容可以将它们与任何模板使用联系起来。

C++ 中模板的核心语义之一是模板 id 具有单个模板参数列表:A<int> 。您提议的更改允许模板 ID 具有多个模板参数列表:A<int><double>

您可以使用模板默认参数在标准C++中实现所需的结果:

template <typename T, typename U = void>
struct A
{
    T t;
};
template <typename U>
struct A<int, U>
{
    int i;
    T t;
};
A<float> a1;
A<int, double> a2;
a1.t = 2.0f;
a2.i = 2;
a2.t = 2.0;

语法A<int, double>A<int><double>更简单 - 单个参数列表而不是多个参数列表 - 这是我不会进行您建议的更改的主要原因。

话虽如此,可以放宽模板的所有显式/部分专用化采用相同数量的模板参数的要求。相反,可能要求专用化至少采用与主模板一样多的模板参数。如果是这种情况,则可以省略主模板中的冗余第二个参数。

template <typename T>
struct A; // primary template, definition omitted for brevity
template <typename U>
struct A<int, U>; // partial specialization, definition omitted for brevity
A<float> a1; // OK, selects primary template
A<int, double> a2; // OK, selects partial specialization
A<float, float> a3; // Error, no valid specialization

我不认为这种变化会破坏任何东西。下一个问题是它是否会改善任何东西?