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
_ptr
An alias for “; - element_type“Is also an alias if
_ptr::element_type
If this type exists, it is_ptr::element_type
This type, if_ptr::element_type
This type does not exist, but_ptr
Is a template specialization, then it is_ptr
Otherwise, it is__undefined
, are actually meaningless types; - difference_type“Is also an alias if
_ptr::difference_type
If this type exists, it is_ptr::difference_type
Otherwise, it isptrdiff_t
Type; - templateusing rebind, it is a type alias template made up of classes
pointer_traits
The 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_arg
If_ptr
It’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_type
Type 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 ~