在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
{ };
有人可以解释_Cp的推论吗?就像魔术一样。
答案 0 :(得分:8)
指向成员的指针的类型为Type Class::*
,其中Type
是所指向的对象类型或函数类型。例如,如果为模板提供int C::*
,则编译器可以通过检查成员指针的类型并查看类类型为C
来简单地推断类类型。它也将以相同的方式将指向的类型推导为int
。它的工作方式与我们人类的工作方式非常相似。通常,我们将这种技术称为pattern matching,您可能会从regular expressions开始熟悉这种技术。
正式:
指向成员的指针类型包括指向的类对象的类型和指向的成员的类型。
如果P和A具有以下形式之一,则可以推导出模板类型参数T,模板模板参数TT或模板非类型参数i:
- [snip]
T T::*
- [snip]
每个[temp.deduct.type]/1的位置:
可以在几种不同的上下文中推导模板参数,但是在每种情况下,将根据模板参数指定的类型(称为P)与实际类型(称为A)进行比较,并尝试对查找模板参数值(类型参数的类型,非类型参数的值或模板参数的模板),在替换推导值(称为推导的A)后,它将使P与答:
答案 1 :(得分:5)
几年前,我对C ++模板专业化的误解基本上与现在一样。
其他答案很好,但我认为当他们了解发生了什么时,它们不会真正帮助我。因此,假设您正遭受与我相同的误解,请让我尝试解释一下我最终如何正确理解自己的想法:
那时,我的直觉理解错误地告诉我,术语“专业化”意味着“专业化模板”在某种程度上应比“原始模板”具有更少的模板参数。这种假设是由以下事实引起的:几乎所有试图解释专业化工作原理的教程代码都始于像这样的示例
template <class T> // <-- one parameter
class MyClass { ... };
template <> // <-- zero parameters
class MyClass<int> { ... };
现在,您的is_member_pointer
示例是一个反例,表明这根本不是真的。
我的全部误解始于使用错误的术语。您可能已经注意到,上面我在“专业模板”和“原始模板”两边加上了引号?我这样做是因为这是错误的,但是那是我当时使用的词。
确实是,只有一个模板。说有两个模板是错误的,一个是原始模板,一个是专用模板。在我的示例中
template <class T>
class MyClass { ... };
是 模板,而
template <>
class MyClass<int> { ... };
是同一模板的专业化 。
使它专业化的原因是在类名后面使用<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>> { ... };
如您所见,专业化比实际模板具有更多的模板参数。只要专业化类型的数量(在此示例中为一种类型,即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>> { ... };
它必须问自己“是否有任何类型A, B, C, D
,以便给定类型(int
)等于专业化类型many_to_one<A, B, C, D>
?答案是否定的,所以它会移动继续下一个专业化:
template <>
class MyClass<int> { ... };
再次,它问自己“是否有任何类型<empty list here>
,以使给定类型(int
)等于专业化类型int
?显然,是的!因为没有甚至更好的匹配专业,它都会选择这一专业。
相反,如果您有MyClass<double>
,这两个专业的两个问题均给出否作为答案,因此将选择“基本”模板。
因此,最后回答您的原始问题:如果您写的是例如
std::is_member_pointer<decltype(&std::string::size)>
编译器查看专业化,然后看到“哦,看。如果我放_Tp = size_t
和_Cp = std::string
,则给定类型decltype(&std::string::size)
等于专业化类型_Tp _Cp::*
,所以我选择了这种专长(恰好继承自std::true_type
)。
但是如果你写例如
std::is_member_pointer<int>
然后,它找不到_Tp
和_Cp
的任何类型来使_Tp _Cp::*
等于int
,因此它放弃了该特殊化,并选择了“基本”模板(恰好是从std::false_type
继承的。)
答案 2 :(得分:4)
没有什么神奇的。这是简单的专业化,并且只要为类成员实例化模板,就可以推论_Cp
包含类。
这是选择最佳可用专业的一般情况的应用。