为什么模板类的成员需要使用其模板类参数进行参数化

5
在 Stroustrup 的书中第四版第一次印刷的第 668 页,您将找到以下模板类的示例:
template<typename C>
class String{
public:
    String();
    ...
private:
    int sz;
    C* ptr;
};

在第679页,作者写道:

Members of a template class are themselves templates parameterized by the parameters of their template class. When such a member is defined outside its class, it must explicitly be declared as template. For example:

template<typename C>
String<C>::String()
    :sz(0), ptr(ch)
{
    ch[0] = {}; 
}

这个例子中有一个明显的错误。变量ch在上面没有任何意义。但这与我的问题无关。我想知道的是,为什么上面的构造函数不能不带参数C来定义,如下所示?

template<typename C>
String::String()
    : sz(0), ptr(nullptr)
{
}
2个回答

9

String 是一个模板的名称,而不是一个类。模板甚至不是一种类型,因此它没有成员。但是,模板特化后就变成了一个类。为了在该定义中指定所引用的特化版本,您需要插入C

现在恰好是该定义本身也是一个模板,但那是因为您正在为特化族定义内容。然而,事实仍然是您需要明确命名这些特化版本。

最后,您只需要指定一次特化版本的原因是,在类模板特化的作用域内,模板名称以一种特殊的方式处理。在该作用域内,模板名称指的是注入类名称,即特化版本本身。这就是为什么...

template<typename C>
String<C>::String<C>()
    :sz(0), ptr(ch)
{
}

...可以写成...

template<typename C>
String<C>::String()
    :sz(0), ptr(ch)
{
}

由于 String<C> 已经建立了我们所指的特化,而且我们在它的作用域内,因此我们可以使用 String 作为注入类名,并赋予其特殊含义。


在标准中的哪个地方说,当模板类的成员在其模板类之外定义时,它是一个特化? - WaldB
@WaldB - [\ temp.class]/3 - StoryTeller - Unslander Monica
@WaldB - 哦,我想我误解了你的意思。并不是说“模板类在定义在外部时就是一个特化”。一般来说,当你写my_temp<C>时,你是在引用一个特化。任何特化都可以被引用,并且也确实被引用了它的模板标识[temp.names]/1 - StoryTeller - Unslander Monica
[temp.spec]/3 表示:“显式具体化声明由 template <>引入”。我在我的问题和你的回答中没有看到任何 template <>,你可以解释一下吗? - WaldB
@WaldB - 抱歉。这是我打开太多标签的结果。段落编号没错,但是区域不对。https://timsong-cpp.github.io/cppwp/n4659/temp.class#3 - StoryTeller - Unslander Monica
显示剩余3条评论

0

C++ 可以设计一条特殊规则,使得这种琐碎而常见的情况按照你的期望工作。然而,当前的规则更加一致,因为它们仍然适用于更复杂的情况。

特别是,请考虑以下情况:

template<typename C, int I> class String { };
template<typename C> class String<C,0> { String(); };
template<typename C> class String<C,1> { String(); };

这是一个带有两个部分特化的类模板。

现在第一个构造函数被定义为

template<typename C> String<C,0> :: String() { }

你会发现模板参数列表有一个额外的参数,0,它表示特定的专门化。部分专门化会改变基本模板的一些但不是全部的模板参数。你必须指定哪些参数可以变化,哪些参数具有固定值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接