在c ++ std库中,is_member_pointer实现为
template<typename _Tp>
struct __is_member_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_pointer_helper<_Tp _Cp::*>
: public true_type { };
/// is_member_pointer
template<typename _Tp>
struct is_member_pointer
: public __is_member_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
Run Code Online (Sandbox Code Playgroud)
有人可以解释_Cp的推导方式吗?它像魔术一样工作。
指向成员的指针的类型为Type Class::*,其中指向的Type是对象类型或函数类型。int C::*例如,如果您提供一个模板,则编译器可以通过检查成员指针的类型并查看类类型为来简单地推断类类型C。它也将以int相同的方式推断出指向类型。它的工作方式与我们人类的工作方式非常相似。通常,我们将这种技术称为模式匹配,您可能会从正则表达式熟悉它。
正式地:
指向成员的指针类型包括指向的类对象的类型和指向的成员的类型。
如果P和A具有以下形式之一,则可以推导出模板类型参数T,模板模板参数TT或模板非类型参数i:
- [片段]
T T::*- [片段]
可以在几种不同的上下文中推导模板参数,但是在每种情况下,将根据模板参数指定的类型(称为P)与实际类型(称为A)进行比较,并尝试查找模板参数替换推导值(称为推导的A)后,将使P与A兼容的值(类型参数的类型,非类型参数的值或模板参数的模板)将与P兼容。
几年前,我对C ++模板专业化的误解基本上与您现在遇到的误解相同。
其他答案很好,但是我不认为它们会真正帮助我了解当时的情况。因此,假设您正遭受与我相同的误解,请让我尝试解释一下我最终如何正确理解自己的想法:
那时,我的直觉理解错误地告诉我,术语“专业化”意味着“专业化模板”在某种程度上应比“原始模板”具有更少的模板参数。这种假设是由以下事实引起的:几乎每个试图解释专业化工作原理的教程代码都以类似
template <class T> // <-- one parameter
class MyClass { ... };
template <> // <-- zero parameters
class MyClass<int> { ... };
Run Code Online (Sandbox Code Playgroud)
现在,您的示例is_member_pointer是一个反例,表明这根本不是真的。
我的全部误解始于使用错误的术语。您可能已经注意到,上面我在“专业模板”和“原始模板”两边加上了引号?我这样做是因为这是错误的,但是那是我当时使用的词。
诚然,只有一个模板。说有两个模板是错误的,一个是原始模板,一个是专用模板。在我的例子中
template <class T>
class MyClass { ... };
Run Code Online (Sandbox Code Playgroud)
是的模板,而
template <>
class MyClass<int> { ... };
Run Code Online (Sandbox Code Playgroud)
是相同模板的专业化版本。
使它专业化的原因是<int>类名称后面的用法。而已!
这是同一模板的另一个有效的专业化:
template <class... Types>
struct many_to_one { ... };
template <class A, class B, class C, class D> // <-- four parameters, could be even more
class MyClass<many_to_one<A, B, C, D>> { ... };
Run Code Online (Sandbox Code Playgroud)
如您所见,专业化比实际模板具有更多的模板参数。只要专业化类型的数量(在此示例中为一种,即many_to_one<A, B, C, D>)与实际模板的模板参数的数量相匹配,这就是完全有效的方法。
现在,如果您MyClass<int>在任何地方使用(例如,声明该类型的变量),编译器会做什么?
它介绍了模板及其所有专业。
首先要注意的是:在此示例中,只有一个模板参数MyClass<int, double, short, float>,即使有四个参数,它也无法编译!但是,这四个参数适用于专业化,而不是对模板。
当编译器遍历所有专业领域并找到
template <class A, class B, class C, class D>
class MyClass<many_to_one<A, B, C, D>> { ... };
Run Code Online (Sandbox Code Playgroud)
它必须问自己“是否有任何类型A, B, C, D,以使给定类型(int)等于专业化类型many_to_one<A, B, C, D>?答案是否定的,因此继续进行下一个专业化:
template <>
class MyClass<int> { ... };
Run Code Online (Sandbox Code Playgroud)
再次,它会问自己“是否有任何类型,<empty list here>以便给定类型(int)等于专业化类型int?显然,是的!由于没有更好的匹配专业化,因此选择该专业化。
相反MyClass<double>,如果您有例如,则两个专业的两个问题都给出否作为答案,因此将选择“基本”模板。
因此,最后回答您的原始问题:如果您编写例如
std::is_member_pointer<decltype(&std::string::size)>
Run Code Online (Sandbox Code Playgroud)
编译器查看特殊化,然后看到“哦,看。如果我放_Tp = size_t,_Cp = std::string然后给定类型decltype(&std::string::size)等于特殊化类型_Tp _Cp::*,那么我选择该特殊化(恰好继承自std::true_type)”。
但是如果你写例如
std::is_member_pointer<int>
Run Code Online (Sandbox Code Playgroud)
那么就无法找到任何类型_Tp,并_Cp做出_Tp _Cp::*等于int,所以它丢弃专业化,选择“基本”模板(恰好继承std::false_type)。