C ++ is_member_pointer实现

时间:2019-03-15 15:26:27

标签: c++ c++11

在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的推论吗?就像魔术一样。

3 个答案:

答案 0 :(得分:8)

指向成员的指针的类型为Type Class::*,其中Type是所指向的对象类型或函数类型。例如,如果为模板提供int C::*,则编译器可以通过检查成员指针的类型并查看类类型为C来简单地推断类类型。它也将以相同的方式将指向的类型推导为int。它的工作方式与我们人类的工作方式非常相似。通常,我们将这种技术称为pattern matching,您可能会从regular expressions开始熟悉这种技术。

正式:

  

[temp.deduct.type]/3.2

     
    

指向成员的指针类型包括指向的类对象的类型和指向的成员的类型。

  
     

[temp.deduct.type]/8

     
    

如果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包含类。

这是选择最佳可用专业的一般情况的应用。