Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ is_member_pointer implementation

Tags:

c++

c++11

In c++ std library, is_member_pointer is implemented as

  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
    { };

Can someone explain that how the _Cp is deduced? It works like a magic.

like image 931
Bill Avatar asked Mar 15 '19 15:03

Bill


3 Answers

The type of a pointer-to-member is Type Class::*, where Type is the object type or function type being pointed to. If you provide the template an int C::*, for example, the compiler can simply deduce the class type by examining the type of the pointer-to-member and seeing the class type is C. It would also deduce the pointed-to type as int the same way. It works very similarly to how we humans would do it. In general, we call this technique pattern matching, which you might be familiar with from regular expressions.

Formally:

[temp.deduct.type]/3.2:

A pointer-to-member type includes the type of the class object pointed to and the type of the member pointed to.

[temp.deduct.type]/8:

A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:

  • [snip]
  • T T::*
  • [snip]

Where per [temp.deduct.type]/1:

Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.

like image 54
chris Avatar answered Oct 26 '22 15:10

chris


Some years ago I basically had the same misunderstanding about C++ template specialization that, apparently, you are having now.

The other answers are great, but I don't think they would have really helped me back then understanding what's going on. So, assuming that you are suffering from the same misunderstanding that I did, let me try to explain how I finally got my thoughts right:

Back then, my intuitive understanding erroneously told me that the term "specialization" means that the "specialized template" should somehow have less template arguments than the "original template". This assumption was driven by the fact that almost every tutorial code that tries to explain how specialization works starts with an example like

template <class T> // <-- one parameter
class MyClass { ... };

template <> // <-- zero parameters
class MyClass<int> { ... };

Now, your example of is_member_pointer is one counterexample showing that this is not true at all.

My entire misunderstanding started with using wrong terminology. You may have noticed that above I put quotes around "specialized template" and "original template"? I did this because it's wrong, but those were the words that I was using back then.

True is, that there is only one template. It's wrong to say that there are two templates, an original one and a specialized one. In my example

template <class T>
class MyClass { ... };

is the template, while

template <>
class MyClass<int> { ... };

is a specialization of that same template.

What makes it a specialization, is the use of <int> behind the class name. That's it!

This is another valid specialization of that same template:

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>> { ... };

As you can see, the specialization has way more template parameters than the actual template. And that's perfectly valid as long as the number of specialization types (one type in this example, namely many_to_one<A, B, C, D>) matches the number of template parameters of the actual template.

Now, what does the compiler do, if you use MyClass<int> anywhere, e.g. for declaring a variable of that type?

It has a look at the template and all specializations of it.

First thing to note: As in this example there is only one template parameter, something like MyClass<int, double, short, float> cannot compile, even though there is a specialization with four parameters! But those four parameters apply to the specialization, not to the template.

When the compiler walks through all the specializations and it finds

template <class A, class B, class C, class D>
class MyClass<many_to_one<A, B, C, D>> { ... };

it has to ask itself "are there any types A, B, C, D so that the given type (int) is equal to the specialization type many_to_one<A, B, C, D>? The answer is No, so it moves on to the next specialization:

template <>
class MyClass<int> { ... };

Again, it asks itself "are there any types <empty list here> so that the given type (int) is equal to the specialization type int? Obviously, yes! Since there are no even better matching specializations, it chooses this one.

If instead you have e.g. MyClass<double>, both questions for the two specializations yield No as the answer, so the "basic" template will be chosen.


So, to finally answer your original question: If you write e.g.

std::is_member_pointer<decltype(&std::string::size)>

the compiler looks at the specialization and sees "Oh, look. If I put _Tp = size_t and _Cp = std::string then the given type decltype(&std::string::size) is equal to the specialization type _Tp _Cp::*, so I pick that specialization (that happens to inherit from std::true_type)".

But if you write e.g.

std::is_member_pointer<int>

then it cannot find any types for _Tp and _Cp to make _Tp _Cp::* equal to int, so it discards that specialization and chooses the "basic" template (that happens to inherit from std::false_type).

like image 37
sebrockm Avatar answered Oct 26 '22 13:10

sebrockm


There is nothing magical about it. It is simple specialization, and _Cp is deduced to containing class, whenever the template is instantiated for the class member.

It is an application of general case of selecting best available specialization.

like image 24
SergeyA Avatar answered Oct 26 '22 15:10

SergeyA