This article takes a look at the GCC library’s template class Pointer_traits and uses it as an example to understand the traits technique.

To clarify, I am using the GCC7.1.0 compiler, and the standard library source code is also in this version.

Again, take a look at the mind map, as follows:

1. Pointer extractor POinter_traits description

Pointer_traits is a class template that was introduced after c++11. We can derive pointer types from rebinding template types that are passed in. Pointer_traits Class templates provide standardized methods for accessing certain properties of class pointer types.

So why bother to single out pointer_traits? Like the memory allocator, pointer_traits is a prerequisite for some containers in the STL, and we won’t be able to get around it while we’re talking about containers, so it’ll help you understand it later.

Why is it called a pointer extractor? I understand that it’s similar to a memory extractor called allocator_traits, which uses template parameters to derive types, traits that also refer to extractions, so I’ll call it a pointer extractor.

2. Pointer extractor source code analysis

The class template Pointer_traits has two versions in the library, one specialized and one unspecialized, and the source code is in the bits/ptr_traits. H header, which is actually contained in memory.

2.1 the specializedpointer_traits

Let’s first analyze the non-specialized version of the source code, as follows:

/ / pointer_traits class template
template<typename _Ptr>
    struct pointer_traits
    {
    private:
	template<typename _Tp>
	using __element_type = typename _Tp::element_type;

      template<typename _Tp>
	using __difference_type = typename _Tp::difference_type;

      template<typename _Tp, typename _Up, typename = void>
	struct __rebind : __replace_first_arg<_Tp, _Up> { };

      // Use the following structure if there is a type in the __void_t argument, otherwise use the above structure
      template<typename _Tp, typename _Up>
	struct __rebind<_Tp, _Up, __void_t<typename _Tp::template rebind<_Up>>>
	{ using type = typename _Tp::template rebind<_Up>; };

    public:
      using pointer = _Ptr;
        
      using element_type
	= __detected_or_t<__get_first_arg_t<_Ptr>, __element_type, _Ptr>;

      using difference_type
	= __detected_or_t<ptrdiff_t, __difference_type, _Ptr>;

      template<typename _Up>
        using rebind = typename __rebind<_Ptr, _Up>::type;

      static _Ptr
      pointer_to(__make_not_void<element_type>& __e)
      { return _Ptr::pointer_to(__e); }

      static_assert(! is_same<element_type, __undefined>::value,"pointer type defines element_type or is like SomePointer<T, Args>");
    };
Copy the code

This code may seem a little confusing at first, but it’s always the same. A class is defined, and it ends up being used by someone else, so for a class type, we just need to understand what its public members do, and we probably know what the class does.

__detected_OR_t is also a type template, declared as follows:

template<typename _Default, template<typename. >class _Op.typename. _Args>
    using __detected_or_t
      = typename__detected_or<_Default, _Op, _Args... >::type;Copy the code

If _Op<_Args… > is a valid type, and that type is _Op<_Args… >, otherwise, _Default.

So for a class template pointer_traits, its public members act as follows:

  • pointerThis is actually the template parameter_ptrAn alias for “;
  • element_type“Is also an alias if_ptr::element_typeIf this type exists, it is_ptr::element_typeThis type, if_ptr::element_typeThis type does not exist, but_ptrIs a template specialization, then it is_ptrOtherwise, it is__undefined, are actually meaningless types;
  • difference_type“Is also an alias if_ptr::difference_typeIf this type exists, it is_ptr::difference_typeOtherwise, it isptrdiff_tType;
  • templateusing rebind, it is a type alias template made up of classespointer_traitsThe template parameters of rebind and the template parameters of rebind determine the final type, if_ptr::rebind<_Up>If this type exists, it is_ptr::rebind<_Up>Otherwise, according to the type template__replace_first_argIf_ptrIt’s template specialization_Template<_Tp, _Types... >, it is_Template<_Tp, _Types... >Otherwise, there is no type;
  • pointer_toIt is a static member function that calls the pointer_to function of template type, so what exactly does that depend on_ptr“, but according to the literal meaning should be getelement_typeType the address of an object.

So basically, class template pointer_traits are just type attributes used to retrieve the template parameter _ptr, so you can work backwards to figure out what attributes the template parameter type needs to have.

2.2 specializedpointer_traits

Next, take a look at the source code implementation of the specialized class template POinter_traits:

template<typename _Tp>
    struct pointer_traits<_Tp*>
    {
      typedef _Tp* pointer;   // Take an individual name for the specialized type
      typedef _Tp  element_type;   // Alias the template type
      typedef ptrdiff_t difference_type; 

      template<typename _Up>
        using rebind = _Up*;

      static pointer
      pointer_to(__make_not_void<element_type>& __r) noexcept
      { return std::addressof(__r); }};Copy the code

Template

using rebind is a template that uses the same alias as the template

using rebind. It fetches a pointer to _Up* directly, and all it does is rebind the type member template alias to get a pointer to _Up from a pointer to _Tp.

After the source code analysis, seems to have a little impression, but we should use it specifically?

3. Simple use of pointer extractor

Let’s start with an example code like this:

#include <memory>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

// Translate the compiled types of GCC into real types
const char* GetRealType(const char* p_szSingleType)
{
    const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr.nullptr.nullptr);
    return szRealType;
}

int main(a)
{
	using ptr = typename std::pointer_traits<int* > : :template rebind<double>;
	ptr p1;
	const std::type_info &info = typeid(p1);
	std::cout << GetRealType(info.name()) << std::endl;
	return 0;
}
Copy the code

This example clearly uses specialized POinter_traits, and uses the rebind attribute to get a pointer to a double from an int. The output looks like this:

double*
Copy the code

Looking at the code above, we still don’t know what pointer_traits really does, and it seems to complicate simple types, but it’s handy to use standard template classes to retrieve pointer types when we don’t know exactly what they are, as we see in the library’s deque.

For unspecialized POinter_traits, take a look at this code:

#include <memory>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <string>

struct test_traits
{
	using element_type = int;
	using difference_type = double;
};

struct test_traits2
{
	using element_type = std::string;
	using difference_type = size_t;
};

const char* GetRealType(const char* p_szSingleType)
{
    const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr.nullptr.nullptr);
    return szRealType;
}

int main(a)
{
	using type1 = typename std::pointer_traits<test_traits>::element_type;
	using type2 = typename std::pointer_traits<test_traits2>::difference_type;
	const std::type_info &info = typeid(type1);
	std::cout << GetRealType(info.name()) << std::endl;
	const std::type_info &info2 = typeid(type2);
	std::cout << GetRealType(info2.name()) << std::endl;
	return 0;
}
Copy the code

Pointer_traits can be useful when the type is unknown. A typical use of pointer_traits is in the library’s Allocator_traits class template, which, as we mentioned earlier, is a memory extractor. Inside the extractor, pointer_traits fetch some allocator type attributes.

4. Traits technique from the perspective of pointer extractor

Traits literally means features, traits, so the whole idea of traits is to fetch attributes of an unknown type. Traits are used in template programming to fetch attributes based on a template type. If you know a type, you don’t need to use traits.

Pointer_traits, for example, is a good example of a technique called traits that can be literally referred to as pointer traits, so an unspecialized POinter_traits is a type trait used to retrieve some class pointer, Generically specialized POinter_traits are used for native pointer types, such as int*.

Let’s take a look at using unspecialized POinter_traits to retrieve the attributes of class Pointers as follows:

#include <memory>
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

const char* GetRealType(const char* p_szSingleType)
{
    const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr.nullptr.nullptr);
    return szRealType;
}

int main(a)
{
	using type = typename std::pointer_traits<std::shared_ptr<int>>::element_type;
	const std::type_info &info = typeid(type);
	std::cout << GetRealType(info.name()) << std::endl;
	return 0;
}
Copy the code

Code output :int, which gets the element_type property of the smart pointer.

Well, this article is for you to introduce here, if you think the content is useful, remember to click a like oh ~