简体   繁体   English

类模板的单个成员可以部分特化吗?

[英]Can a single member of a class template be partially specialized?

I came across an interesting point that I wasn't able to explain or find an explanation for.我遇到了一个有趣的问题,我无法解释或找到解释。 Consider the following template definition (compiled with mingw g++ 4.6.2):考虑以下模板定义(使用 mingw g++ 4.6.2 编译):

template <typename T, typename S>
class Foo
{
public:
    void f(){}
    void g(){}
};

Should we want to, we can fully specialize any single member function:如果我们愿意,我们可以完全特化任何单个成员函数:

template <>
void Foo<char,int>::f() {}

But partial specialization fails with an "invalid use of incomplete type 'class Foo<...>'" error:但是部分特化失败并出现“无效使用不完整类型 'class Foo<...>'”错误:

template <typename T, typename S>
void Foo<T,S*>::f()
{
}

template <typename T>
void Foo<T,int>::f()
{
}

And I can't figure out why.我不明白为什么。 Is it a conscious design decision made to avoid some problem I can't foresee?这是为了避免一些我无法预见的问题而做出的有意识的设计决定吗? Is it an oversight?是疏忽吗?

The notion of partial specialization only exists for class templates (described by §14.5.5) and member templates (ie members of a template class that are themselves template functions, described by §14.5.5.3/2).部分特化的概念只存在于类模板(由 §14.5.5 描述)和成员模板(即模板类的成员,它们本身就是模板函数,由 §14.5.5.3/2 描述)。 It does not exist for ordinary members of class templates, nor does it exist for function templates – simply because it is not described by the Standard.它不存在于类模板的普通成员中,也不存在于函数模板中——仅仅是因为标准没有描述它。

Now, you might argue that by giving the definition of a partial specialization of a member function, such as现在,您可能会争辩说,通过给出成员函数的部分特化的定义,例如

template <typename T>
void Foo<T,int>::f()
{ }

you implicitly define a partial specialization of the class template: Foo<T,int> .隐式定义了类模板的部分特化: Foo<T,int> That, however, is explicitly ruled out by the Standard:然而,这明确的标准排除:

(§14.5.5/2) Each class template partial specialization is a distinct template and definitions shall be provided for the members of a template partial specialization (14.5.5.3). (第 14.5.5/2 节)每个类模板部分特化是一个不同的模板,并且应为模板部分特化 (14.5.5.3) 的成员提供定义。

(§14.5.5.3/1) [...] The members of the class template partial specialization are unrelated to the members of the primary template. (§14.5.5.3/1) [...] 类模板部分特化的成员与主模板的成员无关。 Class template partial specialization members that are used in a way that requires a definition shall be defined;应定义以需要定义的方式使用的类模板部分特化成员; the definitions of members of the primary template are never used as definitions for members of a class template partial specialization.主模板成员的定义永远不会用作类模板部分特化成员的定义。 [...] [...]

The latter implies that it is impossible to implicitly define a partial specialization by simply giving the definition of one of its members: The very existence of that member would not follow from the definition of the primary template, hence defining it is equivalent to defining a member function that wasn't declared , and that isn't allowed (even with non-template classes).后者意味着不可能通过简单地给出其成员之一的定义来隐式定义部分特化:该成员的存在不会从主模板的定义中得出,因此定义它等同于定义一个成员未声明的函数,这是不允许的(即使使用非模板类)。

On the other hand, the notion of explicit specialization (or full specialization , as you call it) exists for member functions of class templates.另一方面,对于类模板的成员函数,存在显式特化(或完全特化,如您所称)的概念。 It is explicitly described by the Standard:标准明确描述了它:

(§14.7.3/1) An explicit specialization of any of the following: (第 14.7.3/1 节)以下任何一项的明确特化:
[...] [...]
— member function of a class template — 类模板的成员函数
[...] [...]
can be declared by a declaration introduced by template<>;可以通过 template<> 引入的声明来声明; [...] [...]

§14.7.3/14 describes the details: §14.7.3/14 描述了细节:

(§14.7.3/14) A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition. (第 14.7.3/14 节) 类模板的成员或成员模板可以为类模板的给定隐式实例显式特化,即使该成员或成员模板是在类模板定义中定义的。 [...] [...]

Hence, for explicit specializations of members, the instantiation of the rest of the class template works implicitly – it is derived from the primary template definition, or any partial specializations if defined.因此,对于成员的显式特化,类模板其余部分的实例化是隐式工作的——它派生自主模板定义,或任何部分特化(如果已定义)。

I tried to find a succinct quote from the standard, but I don't think there is one.我试图从标准中找到一个简洁的引用,但我认为没有。 The fact is, there is no such thing as a partial specialization of a template function (or, for that matter, of a template alias).事实是,不存在模板函数(或者,就此而言,模板别名)的部分特化。 Only class templates can have partial specializations.只有类模板可以有部分特化。

Let's forget about templates for a second.让我们暂时忘记模板。 In C++, there is a big difference between class names and function names.在 C++ 中,类名和函数名之间存在很大差异。 There can only be one definition of a class within a given scope.在一个给定的范围内,一个类只能有一个定义。 (You can have various declarations, but they all refer to the One True Class.) So the name really identifies the class. (您可以有各种声明,但它们都指的是 One True Class。)所以名称真正标识了该类。

A function name, on the other hand, is a kind of group identity.另一方面,函数名是一种组标识。 You can define any number of functions within a scope with exactly the same name.您可以在一个作用域内定义任意数量的具有完全相同名称的函数。 When you use a function name to call a function, the compiler has to figure out which function you really meant by looking at the various possibilities and matching the signature of each of them with the supplied arguments.当您使用函数名来调用函数时,编译器必须通过查看各种可能性并将每个函数的签名与提供的参数进行匹配来确定您真正指的是哪个函数。 There's no relationship between the various functions which share a name;共享名称的各种函数之间没有关系; they're completely separate entities.它们是完全独立的实体。

So, no big deal.所以,没什么大不了的。 You knew all this, right?你知道这一切,对吧? But now let's go back to templates.但现在让我们回到模板。

The name of a templated class is still unique.模板化类的名称仍然是唯一的。 Although you can define partial specializations, you have to explicitly specialize the same templated class.尽管您可以定义部分特化,但您必须显式特化同一个模板化类。 This mechanism looks superficially like the function-name resolution algorithm referred to above, but there are significant differences -- one of them is that, unlike function prototypes, you cannot have two class templates in the same scope with different kinds of template parameters.这种机制表面上看起来像上面提到的函数名解析算法,但有显着的区别——其中之一是,与函数原型不同,在同一作用域中不能有两个具有不同类型模板参数的类模板。

Templated functions, on the other hand, have no need to define unique names.另一方面,模板函数不需要定义唯一的名称。 Templating does not replace the normal function overload mechanism.模板并不能取代正常的函数重载机制。 So when the compiler is trying to figure out what a function name means, it has to consider all templated and non-templated declarations for that function name, resolve the templated ones to a set of template parameter assignments (if possible) and then once it has a list of possible function objects, select the best one with normal overload resolution.因此,当编译器试图找出函数名称的含义时,它必须考虑该函数名称的所有模板化和非模板化声明,将模板化声明解析为一组模板参数分配(如果可能),然后将其解析为有一个可能的函数对象列表,选择具有正常重载分辨率的最佳函数对象。

That's quite a different algorithm from the templated class template parameter resolution.这是一种与模板化类模板参数解析完全不同的算法。 Instead of just matching a list of provided template arguments with a list of declared template parameters, which is how it resolves class templates, it has to take each templated function which might possibly match (has at least the right number of parameters, for example);它不只是将提供的模板参数列表与声明的模板参数列表匹配,这就是它解析类模板的方式,它必须采用每个可能匹配的模板化函数(例如,至少具有正确数量的参数) ; deduce template parameters by unifying the supplied arguments with the template;通过将提供的参数与模板统一来推导模板参数; and then add the resolve specialization to the overload set for a further round of overload resolution.然后将解析特化添加到重载集以进行下一轮重载解析。

I suppose it would have been possible to have added partial specialization resolution into that process as well, but the interactions between partial specialization and function overloading strike me as likely to lead to pseudo-magical behaviour.我想也可以在该过程中添加部分专业化解决方案,但是部分专业化和函数重载之间的相互作用让我觉得很可能会导致伪魔法行为。 In the event, it wasn't necessary and so there is no such mechanism.在这种情况下,没有必要,所以没有这样的机制。 (You can fully specialize a function template. Full specialization means that there are no template arguments to deduce, so it's not a problem.) (你可以完全特化一个函数模板。完全特化意味着没有模板参数可以推导,所以这不是问题。)

So that's the scoop: you can't partially specialize a templated function, but there is nothing stopping you from providing any number of function templates with the same name.所以这就是独家新闻:您不能部分专门化模板化函数,但是没有什么可以阻止您提供任意数量的具有相同名称的函数模板。 All of them will be considered in overload resolution, and the best one will win, as usual.所有这些都将在超载决议中考虑,并且像往常一样,最好的将获胜。

Usually, that's actually sufficient for your overloading needs.通常,这实际上足以满足您的重载需求。 You should think about templated functions just the same way you think about normal functions: come up with a way to select the one you want based on the supplied arguments.你应该像考虑普通函数一样考虑模板化函数:想出一种方法来根据提供的参数选择你想要的函数。 If you feel you really need to supply template parameters in a function call, rather than having them be deduced, just make the function a (possibly static) member of a templated class, and supply the template arguments to the class.如果你觉得你真的需要在函数调用中提供模板参数,而不是让它们被推导,只需让函数成为模板类的(可能是静态的)成员,并向类提供模板参数。

Hope that helps...希望有帮助...

I think that the difference is that when you do the first (valid) explicit specialization of f :我认为不同之处在于,当您对f进行第一次(有效)显式特化时:

template <>
void Foo<char,int>::f() {}

You are doing an implicit instantiation of Foo<char,int> .您正在对Foo<char,int>进行隐式实例化。 But when you try the partial specialization with:但是当您尝试使用以下部分专业化时:

template <typename T>
void Foo<T,int>::f()
{
}

The compiler would need to instantiate implicitly Foo<T,int> before doing the specialization, but it cannot do that because of the T .编译器需要在进行专业化之前隐式实例化Foo<T,int> ,但由于T ,它不能这样做。 And it fails.它失败了。

You can check that is the case with the following code:您可以使用以下代码检查情况是否如此:

template <typename T, typename S>
class Foo
{
public:
    void f(){}
    void g(){}
};


template <>
void Foo<char,int>::f() //line 11
{}

template <>
class Foo<char,int> //line 15
{};

With g++ it gives the errors:使用g++它会给出错误:

test.cpp:15:7: error: specialization of ‘Foo<char, int>’ after instantiation
test.cpp:15:7: error: redefinition of ‘class Foo<char, int>’
test.cpp:2:7: error: previous definition of ‘class Foo<char, int>’

With clang++ is a bit clearer:使用clang++更清楚一点:

test.cpp:15:7: error: explicit specialization of 'Foo<char, int>' after instantiation
class Foo<char,int>
      ^~~~~~~~~~~~~
test.cpp:11:6: note: implicit instantiation first required here
void Foo<char,int>::f() 
     ^

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM