当前位置: 首页 > news >正文

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

一、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中的元编程封装真正用到合适的编程场景中。
坎井之蛙,也有外探宇宙之心!

相关文章:

  • 烟台做网站建设电话/举例说明seo
  • 元氏网站建设/3天引流800个人技巧
  • 新疆建设厅官方网站安装资质备案/中文搜索引擎排名
  • 如何做微信朋友圈网站/网址搜索
  • 网络推广的优势/seo关键词查询工具
  • 通过网站的和报刊建设/网站如何优化一个关键词
  • 业务逻辑漏洞
  • Open3D 点云最小二乘法拟合空间直线(Python版本)
  • 2023 年 10 大最佳 GIS 软件
  • 领域适应(Domain Adaptation)概念与tensorflow2简单实现
  • vite中如何更优雅的使用css
  • C# 目录
  • 【算法基础】1.5 前缀和与差分
  • JS入门到精通 正则详解(11)
  • 第五届字节跳动青训营 前端进阶学习笔记(二)JavaScript编码规范
  • 字节青训营Go语言学习第一天--基础语言+实战案例
  • 小红书找达人如何避坑?怎么知道数据是不是刷出来的
  • 多线程之常见的锁策略