抱歉缺少更好的头衔。
在尝试实现我自己的std::move
版本并理解它有多容易时,我仍然对C ++如何处理部分模板特化感到困惑。我知道它们是如何工作的,但我发现这种规则很奇怪,我想知道其背后的原因。
template <typename T>
struct BaseType {
using Type = T;
};
template <typename T>
struct BaseType<T *> {
using Type = T;
};
template <typename T>
struct BaseType<T &> {
using Type = T;
};
using int_ptr = int *;
using int_ref = int &;
// A and B are now both of type int
BaseType<int_ptr>::Type A = 5;
BaseType<int_ref>::Type B = 5;
如果没有RemoveReference
的部分专精,T
总是T
:如果我给了int &
,它仍然是int &
1}}贯穿整个模板。
但是,部分专用模板似乎会折叠引用和指针:如果我提供了int &
或int *
,并且这些类型与专用模板中的类型匹配,T
只会是int
。
这个功能非常棒,非常有用,但我很好奇,我想知道这个不太明显的怪癖背后的官方推理/规则。
答案 0 :(得分:1)
如果您的模板模式与T&
匹配到int&
,则T&
为int&
,这意味着T
为int
。
特化中的类型T
仅与主模板中的T
相关,因为它用于模式匹配第一个参数。
在专业化中将T
替换为X
或U
可能会让您更加困惑。重用变量名称可能会造成混淆。
template <typename T>
struct RemoveReference {
using Type = T;
};
template <typename X>
struct RemoveReference<X &> {
using Type = X;
};
和X&
匹配T
。如果X&
为T
,T
为int&
,则X
为int
。
为什么标准会说这个?
假设我们看一下不同的模板专业化:
template<class T>
struct Bob;
template<class E, class A>
struct Bob<std::vector<E,A>>{
// what should E and A be here?
};
答案 1 :(得分:0)
部分特化与函数模板非常相似:实际上,重载函数模板经常被误认为是它们的部分特化(这是不允许的)。给定
template<class T>
void value_assign(T *t) { *t=T(); }
然后显然T
必须是没有(最外层)指针状态的参数类型的版本,因为我们需要该类型来计算通过指针分配的值。我们当然不会写value_assign<int>(&i);
来调用这种类型的函数,因为可以推导出参数。
在这种情况下:
template<class T,class U>
void accept_pair(std::pair<T,U>);
请注意,模板参数的数量大于的数量<#34;提供的&#34;作为输入(即,用于扣除的参数类型的数量):复杂的类型可以提供多种类型的价值&#34;信息。
所有这些看起来与类模板非常不同,其中类型必须明确给出(仅sometimes true as of C++17)并且它们在模板中逐字使用(如您所说)。< / p>
但请再次考虑部分专业化:
template<class>
struct A; // undefined
template<class T>
struct A<T*> { /* ... */ }; // #1
template<class T,class U>
struct A<std::pair<T,U>> { /* ... */ }; // #2
这些分别与(不相关的)函数模板value_assign
和accept_pair
完全同构。例如,我们必须编写A<int*>
来使用#1;但这简直类似于调用value_assign(&i)
:特别是,模板参数仍然推导,只是这次来自显式指定的类型int*
而不是类型表达式&i
。 (因为即使提供显式模板参数也需要推断,部分特化必须支持推导其模板参数。)
#2再次说明了在此过程中不保留类型数量的想法:这应该有助于打破错误的印象,即&#34; 模板参数&#34;应继续参考&#34; 提供的类型&#34;。因此,部分特化不仅仅声称一组(通常是无界的)模板参数:它们解释它们。
又一个相似之处:同一类模板的多个部分特化之间的选择是exactly the same,因为它们在重载时丢弃不太具体的函数模板。 (但是,由于在部分特化情况下不会发生重载解析,因此该过程必须除去那里的所有候选者。)