跟我学c++高级篇——模板元编程之四Wrapper

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、Wrapper

c++ Wrapper可以理解为外覆器或者包装器。它既是一种设计方法又是一种元编程应用的技巧这个在c++STL应用非常广泛可以处理类型、控制分支、CV的控制等等。在标准库里有有基础的std::integral_constant还有其它的如引用外覆器等。下面会分别对其进行介绍。
为什么需要外覆器它其是有以下几个作用
1、类型的选择元编程在编译期确定具体的类型
2、逻辑控制元编程在编译期处理无法使用if else
3、Lazy evaluation延迟加载或叫延迟计算

二、常见的包装器

1、整形包装器
先看一下STL中的整形外覆器的实现

template<class T, T v>
struct integral_constant {
    static constexpr T value = v;
    using value_type = T;
    using type = integral_constant; // using injected-class-name
    constexpr operator value_type() const noexcept { return value; }
    constexpr value_type operator()() const noexcept { return value; } // since c++14
};

STL中的整形类型包装器和真实Boost库的略微有些差别可能出于功能最小原则把一些迭代功能删除了。同时把一些操作运算符如equal等挪到了算法库里。
提到整形外覆器就必须得提到bool型的别名模板std::bool_constant以及两个特化true_type和false_type。有兴趣可以看看他们的实现及应用都很简单正好实现了开头提到的功能。

2、STL中的相关外覆器
在标准库里有很多类似的外覆器看下面的例子
引用外覆器从std::reference_wrapper

namespace detail {
template <class T> T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}

template <class T>
class reference_wrapper {
public:
  // 类型
  using type = T;

  // 构造/复制/销毁
  template <class U, class = decltype(
    detail::FUN<T>(std::declval<U>()),
    std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
  )>
  constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
    : _ ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
  reference_wrapper(const reference_wrapper&) noexcept = default;

  // 赋值
  reference_wrapper & operator=(const reference_wrapper& x) noexcept = default;

  // 访问
  constexpr operator T& () const noexcept { return * _ ptr; }
  constexpr T& get() const noexcept { return * _ ptr; }

  template< class... ArgTypes >
  constexpr std::invoke_result_t<T&, ArgTypes...>
    operator() ( ArgTypes&&... args ) const {
    return std::invoke(get(), std::forward<ArgTypes>(args)...);
  }

private:
  T* _ptr;
};

// 推导指引
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;

从这个可以推导出std::ref, std::cref这两个写过std::thread的应该熟悉。另外还有拷贝外覆器(Copyable wrapper-C++20),std::type_index等等。

三、例程

外覆器的例程有很多看一下cppreference上的例程

#include <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <numeric>
#include <random>
#include <functional>

int main()
{
    std::list<int> l(10);
    std::iota(l.begin(), l.end(), -4);

    std::vector<std::reference_wrapper<int>> v(l.begin(), l.end());
    // 不能在 list 上用 shuffle 要求随机访问但能在 vector 上使用它
    std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});

    std::cout << "Contents of the list: ";
    for (int n : l) std::cout << n << ' '; std::cout << '\n';

    std::cout << "Contents of the list, as seen through a shuffled vector: ";
    for (int i : v) std::cout << i << ' '; std::cout << '\n';

    std::cout << "Doubling the values in the initial list...\n";
    for (int& i : l) {
        i * = 2;
    }

    std::cout << "Contents of the list, as seen through a shuffled vector: ";
    for (int i : v) std::cout << i << ' '; std::cout << '\n';
}

上面是引用外覆器的下面再看一个std::type_index的

#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <string>
#include <memory>

struct A {
    virtual ~A() {}
};

struct B : A {};
struct C : A {};

int main()
{
    std::unordered_map<std::type_index, std::string> type_names;

    type_names[std::type_index(typeid(int))] = "int";
    type_names[std::type_index(typeid(double))] = "double";
    type_names[std::type_index(typeid(A))] = "A";
    type_names[std::type_index(typeid(B))] = "B";
    type_names[std::type_index(typeid(C))] = "C";

    int i;
    double d;
    A a;

    // 注意我们正在存储指向类型 A 的指针
    std::unique_ptr<A> b(new B);
    std::unique_ptr<A> c(new C);

    std::cout << "i is " << type_names[std::type_index(typeid(i))] << '\n';
    std::cout << "d is " << type_names[std::type_index(typeid(d))] << '\n';
    std::cout << "a is " << type_names[std::type_index(typeid(a))] << '\n';
    std::cout << "b is " << type_names[std::type_index(typeid(*b))] << '\n';
    std::cout << "c is " << type_names[std::type_index(typeid(*c))] << '\n';
}

基础的东西单纯拿出来看相当枯燥但不理解这些看一些组合代码时就会晕菜。

四、总结

Wrapper不管如何翻译只要明白了它的含义就好其实有些东西本来就很难达到中英完全相通的地步。Wrapper是一种实现静态多态的基础手段通过Wrapper可以在元编程中有效控制类型和逻辑运算。包装器是一项非常基础的构建工具把它弄清楚掌握好才能更好的进行相关元编程开发。特别对STL中的一些基础的包装器的意义理解到位才能把STL中的元编程封装真正用到合适的编程场景中。
坎井之蛙也有外探宇宙之心

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: c++