c++ is_member_pointer implementation

c++ is_member_pointer implementation

本文关键字:implementation pointer member c++ is      更新时间:2023-10-16

在 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是如何推断的吗? 它就像一个魔术。

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

正式:

[温度扣除类型]/3.2:

指向

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

[温度扣除类型]/8:

如果 P 和 A 具有以下形式之一,则可以推导出模板类型参数 T、模板模板参数 TT 或模板非类型参数 i:

  • [截图]
  • T T::*
  • [截图]

其中 [温度扣除类型]/1:

可以在几个不同的上下文中推导模板参数,但在每种情况下,将根据模板参数指定的类型(称为 P)与实际类型(称为 A)进行比较,并尝试查找模板参数值(类型参数的类型、非类型参数的值、 或模板参数的模板),这将使 P 在替换推导值(称为推导的 A)后与 A 兼容。

几年前,我对模板专业化基本上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)。

它没有什么神奇的。它是简单的专用化,每当为类成员实例化模板时,_Cp都会被推导出为包含类。

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