我碰到一个有趣的问题,我是不是能够解释或找到一个解释。 请看下面的模板定义(编译使用MinGW的g ++ 4.6.2):
template <typename T, typename S>
class Foo
{
public:
void f(){}
void g(){}
};
如果我们愿意,我们可以完全专注任何单一的成员函数:
template <>
void Foo<char,int>::f() {}
但部分特失败“无效使用不完全类型‘类Foo <...>’的”错误:
template <typename T, typename S>
void Foo<T,S*>::f()
{
}
template <typename T>
void Foo<T,int>::f()
{
}
我想不通为什么。 难道以避免一些问题,我不能预见有意识的设计决策? 它是一个监督?
提前致谢。
偏特的概念只存在于类模板(通过§14.5.5所述)和构件的模板(即,模板类的成员本身是模板函数,由§14.5.5.3/ 2所描述的)。 它不存在的类模板的普通成员,也不存在函数模板 - 只是因为它不是由标准描述。
现在,你可能认为,通过给一个成员函数的偏特的定义,如
template <typename T>
void Foo<T,int>::f()
{ }
你隐式地定义的类模板的部分特: Foo<T,int>
。 然而,这是明确的标准排除:
(§14.5.5/ 2)每一类模板部分特例是一种独特的模板,并应的模板部分特(14.5.5.3)的成员提供的定义。
(§14.5.5.3/ 1)[...]类模板偏特的成员是无关的主模板的成员。 被用在需要定义将被定义的方式中使用的类模板部分特例成员; 主模板的成员的定义从未用作类模板部分特例的成员的定义。 [...]
后者意味着,这是不可能通过简单地给予其成员中的一个的定义,以隐式地定义的局部专业化:该成员的存在本身就不会从主模板的定义遵循的,因此将其定义为等同于定义一个构件功能没有被宣布 ,这是不允许的(甚至非模板类)。
在另一方面, 明确的分工 (或完全专业化 ,正如你所说的话)的概念存在的类模板成员函数。 它是明确的标准描述:
(§14.7.3/ 1)的下列任何的显式专业化:
[...]
- 类模板的成员函数
[...]
可以通过模板引入声明<>被声明; [...]
§14.7.3/ 14描述的细节:
(§14.7.3/ 14)的成员或类模板的成员模板可被明确地专用于类模板的一个给定的隐式实例,即使部件或构件的模板是在类模板定义所定义。 [...]
因此,对于成员的显式特殊化,类模板的其余部分的实例化作品隐式 - 它是从主模板定义,或任何偏特衍生如果定义。
我试图找到从标准简洁的报价,但我不认为这是一个。 事实是,有一个模板函数的偏特(或者,对于这个问题,模板别名)没有这样的事。 只有类模板可以有部分特例。
让我们忘掉了2模板。 在C ++中,有类名和函数名之间有很大的区别。 只能有一个给定的范围内一类的一个定义。 (你可以有不同的声明,但他们都指的是一个真正的类。)所以,名字还真标识类。
一个函数的名称,在另一方面,是一种群体认同的。 您可以在范围内定义任意数量的功能完全相同的名称。 当您使用函数名调用函数,编译器必须找出哪些工作,你真的看各种可能性和他们每个人的签名与提供的参数匹配的意思。 这里面有一个共享名称的各种功能之间没有任何关系; 他们是完全独立的实体。
所以,没什么大不了的。 你知道这一切,不是吗? 但现在,让我们回到模板。
一个模板类的名称仍然是唯一的。 虽然你可以定义部分特例,你必须明确专攻同一模板类。 这种机制表面上看起来像上面提到的功能名称解析算法,但也有显著差异 - 其中之一是,与函数原型,你不能在不同类型的模板参数范围相同两类模板。
为模板的功能,而另一方面,有没有必要定义唯一的名称。 模板不能取代正常的函数重载机制。 所以,当编译器试图找出一个函数名字的意思是,它必须考虑该功能名称的所有模板和非模板声明,解决模板的人一组模板参数分配(如果可能的话),然后一旦有可能的功能对象的列表中,选择最好的一个正常的重载解析。
这是从模板类模板参数分辨率完全不同的算法。 而不是只匹配提供的模板参数列表与声明的模板参数列表,这是它如何解决类模板,它必须采取每个模板的功能,有可能会匹配(至少有参数的权数,例如) ; 通过统一与模板提供的参数推断模板参数; 然后决心专业化添加到再一轮的重载决议所设定的过载。
我想这将是可能增加了部分专业化分辨率到该进程为好,但局部的专业化和函数重载之间的相互作用让我最有可能导致伪神奇的行为。 在活动现场,这是没有必要的,所以不存在这样的机制。 (你可以完全专注函数模板。完全专业化意味着没有模板参数推断,所以它不是一个问题。)
所以这是独家新闻:你不能部分专业模板化的功能,但并没有什么提供任何数量的函数模板具有相同名称阻止你。 他们都将在重载被认为是,最好的一个赢,像往常一样。
通常情况下,这对您的超载需求实际上就足够了。 你应该想想模板功能,只是你想正常的运行方式相同:想出一个办法来选择您根据所提供的参数想要的。 如果你觉得你真的需要在一个函数调用来提供模板参数,而不是让他们可以推断,只要功能的模板类的(可能是静态)成员,并提供模板参数的类。
希望可以帮助...
我认为,不同的是,当你要做的第一件(有效)明确的专业化f
:
template <>
void Foo<char,int>::f() {}
你正在做的隐式实例Foo<char,int>
。 但是当你试图部分特用:
template <typename T>
void Foo<T,int>::f()
{
}
编译器将需要隐式实例化Foo<T,int>
做专业化之前,但它不能做,因为那T
。 它失败。
您可以检查与下面的代码的情况下:
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
{};
随着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>’
随着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()
^