简体   繁体   English

部分模板特化类型折叠规则

[英]Partial template specialization type collapsing rules

Sorry for the lack of a better title. 抱歉,缺少更好的标题。

While trying to implement my own version of std::move and understanding how easy it was, I'm still confused by how C++ treats partial template specializations. 在尝试实现自己的std::move版本并理解它的简单性时,我仍然对C ++如何对待部分模板专业化感到困惑。 I know how they work, but there's a sort of rule that I found weird and I would like to know the reasoning behind it. 我知道它们是如何工作的,但是我发现有些规则很奇怪,我想知道其背后的原因。

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;

If there wasn't no partial specializations of RemoveReference , T would always be T : if I gave a int & it would still be a int & throughout the whole template. 如果没有RemoveReference部分专业知识,则T始终为T :如果我给出一个int &并且在整个模板中仍为int &

However, the partial specialized templates seem to collapse references and pointers: if I gave a int & or a int * and if those types match with the ones from the specialized template, T would just be int . 但是,部分专用模板似乎折叠了引用和指针:如果我给出了int &int * ,并且这些类型与专用模板中的类型匹配,则T仅为int

This feature is extremely awesome and useful, however I'm curious and I would like to know the official reasoning / rules behind this not so obvious quirk. 此功能非常棒且有用,但是我很好奇,我想知道这个不太明显的怪癖背后的官方推理/规则。

If your template pattern matches T& to int& , then T& is int& , which implies T is int . 如果您的模板模式将T&int&匹配,则T&int& ,这意味着Tint

The type T in the specialization only related to the T in the primary template by the fact it was used to pattern match the first argument. 专业化中的类型T仅与主要模板中的T有关,因为它用于对第一个参数进行模式匹配。

It may confuse you less to replace T with X or U in the specializations. 在专业领域中,用XU代替T可能会减少您的困惑。 Reusing variable names can be confusing. 重用变量名可能会造成混淆。

template <typename T>
struct RemoveReference {
  using Type = T;
};

template <typename X>
struct RemoveReference<X &> {
  using Type = X;
};

and X& matches T . X&匹配T If X& is T , and T ia int& , then X is int . 如果X&T ,并且T ia为int& ,则Xint


Why does the standard say this? 标准为什么这么说?

Suppose we look af a different template specialization: 假设我们看到了另一种模板专业化:

template<class T>
struct Bob;

template<class E, class A>
struct Bob<std::vector<E,A>>{
  // what should E and A be here?
};

Partial specializations act a lot like function templates : so much so, in fact, that overloading function templates is often mistaken for partial specialization of them (which is not allowed). 部分专业化的行为与功能模板非常相似:事实上,重载功能模板常常被误认为是对其进行部分专业化(这是不允许的)。 Given 特定

template<class T>
void value_assign(T *t) { *t=T(); }

then obviously T must be the version of the argument type without the (outermost) pointer status, because we need that type to compute the value to assign through the pointer. 那么显然T必须是没有(最外)指针状态的参数类型的版本,因为我们需要该类型来计算通过指针分配的值。 We of course don't typically write value_assign<int>(&i); 我们当然通常不会写value_assign<int>(&i); to call a function of this type, because the arguments can be deduced. 调用此类型的函数,因为可以推导参数。

In this case: 在这种情况下:

template<class T,class U>
void accept_pair(std::pair<T,U>);

note that the number of template parameters is greater than the number of types "supplied" as input (that is, than the number of parameter types used for deduction): complicated types can provide "more than one type's worth" of information. 请注意,模板参数的数量大于作为输入的“提供”类型的数量(即,大于用于推导的参数类型的数量):复杂类型可以提供“一种以上的价值”的信息。

All of this looks very different from class templates , where the types must be given explicitly (only sometimes true as of C++17 ) and they are used verbatim in the template (as you said). 所有这些看起来与类模板非常不同,在类模板中 ,必须显式地指定类型( 从C ++ 17开始有时是正确的 ),并且在模板中逐字使用它们(如您所说)。

But consider the partial specializations again: 但是,请再次考虑部分专业化:

template<class>
struct A;                               // undefined
template<class T>
struct A<T*> { /* ... */ };             // #1
template<class T,class U>
struct A<std::pair<T,U>> { /* ... */ }; // #2

These are completely isomorphic to the (unrelated) function templates value_assign and accept_pair respectively. 它们分别与(不相关的)功能模板value_assignaccept_pair完全同构。 We do have to write, for example, A<int*> to use #1; 我们必须编写例如A<int*>来使用#1; but this is simply analogous to calling value_assign(&i) : in particular, the template arguments are still deduced , only this time from the explicitly-specified type int* rather than from the type of the expression &i . 但这仅类似于调用value_assign(&i) :特别是,模板参数仍然被推导 ,只是这次是从显式指定的类型int*而不是从表达式&i的类型推导的 (Because even supplying explicit template arguments requires deduction, a partial specialization must support deducing its template arguments.) (因为即使提供显式模板参数也需要推导,所以部分专业化必须支持推导其模板参数。)

#2 again illustrates the idea that the number of types is not conserved in this process: this should help break the false impression that " the template parameter" should continue to refer to " the type supplied". #2再次说明的想法,类型的数量在这个过程中不守恒:这应该有助于打破的虚假印象的“ 模板参数”应继续提及“ 提供类型”。 As such, partial specializations do not merely claim a (generally unbounded) set of template arguments: they interpret them. 因此,部分专业化不仅仅要求(通常是无限制的)模板参数集:它们还会解释它们。

Yet another similarity: the choice among multiple partial specializations of the same class template is exactly the same as that for discarding less-specific function templates when they are overloaded. 另一个相似之处是:同一类模板的多个部分专业化选项之间的选择与重载时丢弃不那么特定的功能模板的选择完全相同 (However, since overload resolution does not occur in the partial specialization case, this process must get rid of all but one candidate there.) (但是,由于在部分专业化的情况下不会发生重载解决方案,因此该过程必须除去那里的所有候选对象,但所有候选对象除外。)

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

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