有什么需要有两种不同的语法来专门化 class 模板的数据成员

What is the need for having two different syntaxes for specializing data member of a class template

我正在编写一个涉及 class 模板特化的示例,我注意到该标准允许两种不同的语法用于数据成员的特化,如下所示:

template<typename T >
struct C {
    static int x;
};
template<> 
struct C<bool> {
    static int x;
};
//here we cannot write prefix template<> 
int C<bool>::x = 0;

从上面的例子可以看出,我们不允许写前缀template<>来定义静态数据成员x。我理解这一点并且对上面的例子没有问题。但是当我修改示例如下所示时,我很惊讶地看到我们 allowed/required 写前缀 template 来定义静态数据成员 x:

template<typename T >
struct C {
    static int x;
};

template<>  //here why is the use of prefix template<> allowed/required? Why standard mandates that we cannot omit this prefix template<> here but in the previous example we can omit it
int C<bool>::x = 0;

从上面的修改示例可以看出,我们需要使用前缀template<>来定义同一个静态数据成员x我的问题是为什么在上面两个例子中定义相同数据成员的语法不同x。这就是标准要求在第二个示例中使用前缀 template<> 的原因。为什么我们不能像第一个示例一样在第二个示例中省略该前缀。 在第二个例子中使用前缀 template<> 是否有助于解决一些问题(比如一些歧义或其他问题)。

PS:请注意,我的问题是 而不是关于 标准中的哪条语句允许这种用法(因为我已经知道使用下面给出的来自 cppreference 的引用语句)但关于允许这两种语法的基本原理(一种使用 template<> 一种不使用)。


我还遇到了 following 和上述 link 中给出的代码示例,它们解释了如何允许这样做:

When defining a member of an explicitly specialized class template outside the body of the class, the syntax template<> is not used, except if it's a member of an explicitly specialized member class template, which is specialized as a class template, because otherwise, the syntax would require such definition to begin with template required by the nested template .

上面引用的语句连同此处给出的示例一起解释了标准如何允许在我的问题中使用上面给出的示例。但我正在寻找第二个示例中标准强制使用前缀 template<> 的原因。为什么标准不允许在第二个示例中省略前缀 template<>,如示例 1.

第一个

template<typename T >
struct C {
    static int x;
};

// This is the template specialisation for bool and all struct members
template<> 
struct C<bool> {
    static int x;
};

// This is the extraneous template specialisation for bool
// and the single struct member, already specialized above - error
// template<> 
// int C<bool>::x = 0;

修改后的第一个例子

template<typename T >
struct C {
    static int x;
};

// This is the template specialisation for bool and the single struct member
template<> 
int C<bool>::x = 0;

// This is the extraneous template specialisation for bool
// and all struct members, one of them is already specialized above - error
// template<> 
// struct C<bool> {
//    static int x;
// };

第二个

template<typename T >
struct C {
    static int x;
};

// This is the unique template specialisation for bool - ok
template<>
int C<bool>::x = 0;

另一个例子

template<typename T >
struct C {
    static int x;
};

// This is the template specialisation for bool
template<> 
struct C<bool> {
    static int x;
};

// This is another template socialisation for int - ok
template<>
int C<int>::x = 0;

它们不是同一事物的不同语法,它们是不同事物的不同语法。

第一个特化整个结构,包括它的所有成员。
也就是说,特化 C<bool>::x 已经隐含在结构特化中,您不能特化两次。
int C<bool>::x = 0; 是该专业化中静态成员的常规定义。

第二种只特化成员,不特化struct,同时定义。

class 模板的显式特化 声明不是 模板声明[Source]。这意味着当我们为 class 模板提供显式特化时,它的行为就像普通的 class.

现在我们可以将其应用于所给出的示例。

示例 1

template<typename T >
struct C {
    static int x;
};
//this is an specialization and so we must use template<> prefix
template<> 
struct C<bool> {
    static int x;
};
//this is just ordinary out of class definition of data member `x`. That is, this is not a specialization and so we don't need the prefix template<>
int C<bool>::x = 0;

在上面的示例中,首先我们为 class 模板 C 本身的 bool 提供了一个显式特化,其行为类似于普通的 class(在感觉它不是模板声明)。这里需要 template<> 前缀,因为我们提供的是专业化。接下来,我们提供我们提供的特化的静态数据成员 x 普通 out-of-class 定义 。但是这里我们不需要 template<> 前缀,因为这不是特化。这只是静态数据成员的普通 out-of-class 定义。

示例 2

template<typename T >
struct C {
    static int x;
};

template<>  // needed because this is not an ordinary out-of-class definition of data member x. Instead this is a specialization 
int C<bool>::x = 0;

在上面的示例中,我们没有为 class 模板 C 提供任何显式特化。相反,我们直接为静态数据成员 x 提供特化。这意味着与示例 1 不同,这里我们没有提供静态数据成员的 out-of-class 定义,而是提供了一个特化。所以在这种情况下我们需要 template<> 前缀。

TLDR

在示例 1 中 int C<bool>::x = 0; 只是一个普通的 out-of-class 静态数据成员定义 x 而不是特化,所以我们不需要前缀 template<> 为了这。相反,在示例 2 中,template<> int C<bool>::x = 0; 是静态数据成员 x 的特化,而不是普通的 out-of-class 定义。所以在这种情况下(示例 2)需要前缀 template<>