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

时间:2014-12-16 11:56:38

标签: c++ templates c++11 language-lawyer

Where in the C++11 standard does it prohibit 'template <typename T> class A {...}; template <typename T> class A<int> {...};' (if anywhere)?中,已确认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,在声明的变量a2的情况下,它是double

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

  • (差不多)没有真正有用的场景
  • 要求当前编译器支持
  • 太复杂了
  • 与其他语言功能存在太多潜在冲突,需要许多新规则
  • 实际上,第四种可能性 - 上述语法和语义在某些实际场景中会很有用,可以合理地包含在标准中,但它不是当前标准的一部分。< / LI>

我想知道为什么C ++不支持这一点 - 我是否认为我的一个要点提供了答案?

4 个答案:

答案 0 :(得分:5)

噢,这会以有趣的方式爆炸。

为清楚起见,我假设您的意思是使用您的示例代码,

 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标准。

你可能会在这些方面想出更多的东西。以这种方式模糊类和类模板之间的界限必将打破各种各样的东西。

答案 1 :(得分:3)

C ++中的名称代表一种实体,这种名称通过名字查找而闻名。

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

这是语言的有用属性。打破这个属性意味着语言在复杂性方面会有很大的提升(对于编译器和用户来说都是如此)。

因此,即使你能找到所有边缘案例并解决Wintermute指出的问题,它仍然很可能被拒绝,因为它使语言更不规则,更令人惊讶,更不统一。 C ++已经受到相当多的不规则性的负担;添加更多内容是没有意义的。

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

答案 2 :(得分:1)

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

专业化中引入的任何其他模板参数都只是悬而未决,没有任何内容可以将它们连接到任何模板使用。

答案 3 :(得分:1)

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

您可以使用模板default-arguments在标准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

我不认为这种改变会破坏任何事情。接下来的问题是它是否会改善什么?