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

一、Wrapper

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

二、常见的包装器

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

template
struct integral_constant {static constexpr T value = v;using value_type = T;using type = integral_constant; // using injected-class-nameconstexpr 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  T& FUN(T& t) noexcept { return t; }
template  void FUN(T&&) = delete;
}template 
class reference_wrapper {
public:// 类型using type = T;// 构造/复制/销毁template (std::declval()),std::enable_if_t>>())>constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN(std::forward(u)))): _ ptr(std::addressof(detail::FUN(std::forward(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_toperator() ( ArgTypes&&... args ) const {return std::invoke(get(), std::forward(args)...);}private:T* _ptr;
};// 推导指引
template
reference_wrapper(T&) -> reference_wrapper;

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

三、例程

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

#include 
#include 
#include 
#include 
#include 
#include 
#include int main()
{std::list l(10);std::iota(l.begin(), l.end(), -4);std::vector> 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 
#include 
#include 
#include 
#include 
#include struct A {virtual ~A() {}
};struct B : A {};
struct C : A {};int main()
{std::unordered_map 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 b(new B);std::unique_ptr 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中的元编程封装真正用到合适的编程场景中。
坎井之蛙,也有外探宇宙之心!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章