第23课 可变参数模板(4)_Optional和Lazy类的实现

1. optional类的实现

(1)optional的功能

  ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有。只有当optional被T初始化之后,这个optional才是有效的。否则是无效的。它实现了未初始化的概念

  ②optional可以用于解决函数返回无效值的问题。当函数返回一个未初始化的Optional对象时,表明函数正确执行了,只是结果不是有用的值。

  ③举例:optional<int> op; //未被初始化。 optional<int> op = 1; //初始化。

(2)实现细节

  ①由于optional<T>需要容纳T的值,所以需要一个缓冲区来保存它,但考虑到内存对齐,需要将T指定在对齐的位置上。可以通过std::alignment_of <T>::value来获取T的内存对齐大小。并通过std::aligned_storage<sizeof(T), aligned(T)>来定义T的内存对齐类型(该模板的本质就重新定义T经对齐后的一种新类型)。

template<unsigned size, unsigned alignment>
struct aligned_storage
{
  using type = struct { alignas(alignment) unsigned char data[size]; };
};

  ②std::aligned_storage一般和placement_new结合使用(见Optional类的create函数),用于初始化由std::aligned_storage定义的一片内存空间。

  ③增加一个m_hasInit标记来记录T空间是否己经初始化。

【编程实验】Optional类的实现

//Optional.hpp

#ifndef _OPTIONAL_H_
#define _OPTIONAL_H_

#include <type_traits>
#include <utility>  //for std::forward
#include <stdexcept>

template <typename T>
class Optional
{
    //std::alignment_of<T>::value获取T的内存对齐大小,std::aligned_storage将T重定义为对齐后的类型
    using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
private:
    data_t m_data;  //内存对齐缓冲区
    bool m_hasInit; //是否己经初始化
private:
    //调用placement_new来创建T对象
    template<class... Args>
    void create(Args... args) //可以接收左右值
    {
        new (&m_data) T(std::forward<Args>(args)...); //调用T的构造函数来初始化m_data空间
        m_hasInit = true;
    }

    //销毁缓冲区的对象
    void destroy()
    {
        if(m_hasInit){
            m_hasInit = false;
            ((T*)(&m_data))->~T(); //调用T的析构函数
        }
    }

    //缓冲区的拷贝
    void copy(const data_t& val)
    {
        destroy();
        new (&m_data) T(*((T*)(&val)));
    }

    //缓冲区的移动
    void move(data_t&& val)
    {
        destroy();

        //调用T的移动构造函数进行移动
        new (&m_data) T(std::move(*((T*)(&val))));
    }

    //Optional赋值操作(左值版本)
    void assign(const Optional& other)
    {
        if(other.isInit()){
            copy(other.m_data);
            m_hasInit = true;
        }else{
            destroy();
        }
    }

    //Optional赋值操作(右值版本)
    void assign(Optional&& other)
    {
        if(other.isInit()){
            move(std::move(other.m_data));
            m_hasInit = true;

            other.destroy(); //other失去资源控制权
        }else{
            destroy();
        }
    }

public:
    Optional():m_hasInit(false){};
    Optional(const T& v)
    {
        create(v);
    }

    Optional(T&& v) : m_hasInit(false)
    {
        create(std::move(v));
    }

    Optional(const Optional& other) : m_hasInit(false)
    {
        if(other.isInit()){
            assign(other);
        }
    }

    Optional(Optional&& other) : m_hasInit(false)
    {
        if(other.isInit()){
            assign(std::move(other));
            other.destroy();
        }
    }

    //根据参数创建对象,如emplace(1,2);
    template<class ...Args>
    void emplace(Args&& ...args)
    {
        destroy();
        create(std::forward<Args>(args)...);
    }

    Optional& operator=(const Optional& other)
    {
        assign(other);
        return *this;
    }

    Optional& operator=(Optional&& other)
    {
        assign(std::move(other));
        return *this;
    }

    explicit operator bool() const //类型转换函数,如if(op)
    {
        return isInit();
    }

    T& operator*()
    {
        if(isInit()){
            return *((T*)(&m_data));
        }

        throw std::logic_error{"try to get data in a Optional which is not initialized"};
    }

    const T& operator*() const
    {
        if(isInit()){
            return *((T*)(&m_data));
        }

        throw std::logic_error{"try to get data in a Optional which is not initialized"};
    }

    T* operator->()
    {
        return &operator*();
    }

    const T* operator->() const
    {
        return &operator*();
    }

    bool operator==(const Optional<T>& rhs) const
    {
        bool bInit = bool(*this);
        return ( !bInit != (!rhs) ? //*this和rhs中一个初始化,一个未初始化
                 false :
                 !bInit ? true : (*(*this) == (*rhs)) //两者都未初始化,返回true
                                                      //两者都初始化时,比较两个T对象是否相等
               );
    }

    bool operator<(const Optional<T>& rhs) const
    {
        bool bInit = bool(*this);
        return !rhs ? false : (!bInit ? true : (*(*this) < (*rhs)));
    }

    bool operator!=(const Optional<T>& rhs) const
    {
        return !(*this == rhs);
    }

    bool isInit() const {return m_hasInit;}

    ~Optional()
    {
        destroy();
    }

};

#endif

//main.cpp

#include "Optional.hpp"
#include <iostream>

using namespace std;

struct Test
{
    Test() : m_a(0), m_b(0){}
    Test(int a, int b) : m_a(a), m_b(b){}

    int m_a;
    int m_b;
    void show()
    {
        cout << "a = "<< m_a << ", b = " << m_b << endl;
    }
};

void TestOptional()
{
    const Optional<string> a("ok");
    Optional<string> b("ok");
    Optional<string> c("aa");
    Optional<string> d = b;
    Optional<string> e;

    cout << (e<b) << endl;  //true
    cout << (b==d) << endl; //true
    cout << *c << endl;
    //cout << *e << endl; //error

    Optional<Test> op;
    op.emplace(1, 2);
    (*op).show();

    Test t;
    if(op)     //判断op是否被初始化
       t = *op;
    t.show();

    op.emplace(3, 4);
    t = *op;
    t.show();
}

int main()
{
    TestOptional();
    return 0;
}

/*输出结果:
e:\Study\C++11\23>g++ -std=c++11 test.cpp
e:\Study\C++11\23>a.exe
1
1
aa
a = 1, b = 2
a = 1, b = 2
a = 3, b = 4
*/

2. 惰性求值性:Lazy类的实现

(1)Lazy类的功能

  ①惰性求值一般用于函数式编程语言中。

  ②可实现函数的延迟调用,函数参数被绑定后并不立即调用,而是在以后的某个时候调用。

  ③可实现大对象数据的延迟加载。如当初始化某个对象时,该对象引用了一个大对象,但很多时候并不马上获取该对象的数据,就可以延迟加载这个大对象。

(2)实现细节

  ①借助lambda表达式,将函数封装到lambda表达式中,而不是马上求值,在需要的时候再调用lambda表达式去求值

  ②std::function用于保存传入的函数,并延迟到后面需要使用值的时候才执行,函数的返回值放到一个Optional对象中。Optional对象是否被初始化,来判断大对象是否己加载。

  ③辅助函数lazy的作用是方便使用Lazy类, Lazy<T>中的T用来表示返回值类型大对象的类型这也是被封装的函数返回值类型,可利用std::result_of来获取该返回值类型。

【编程实验】Lazy类的实现

//Lazy.hpp

#ifndef _LAZY_H_
#define _LAZY_H_

#include "Optional.hpp"
#include <functional>
#include <type_traits>
#include <utility>  //for std::forward

template<typename T>
struct Lazy
{
private:
    Optional<T> m_value;
    std::function<T()> m_func;
public:
    Lazy(){}

    //保存需要延迟执行的函数及其参数
    template<typename Func, typename ...Args>
    Lazy(Func&& f, Args&&... args)
    {
        m_func = [&f, &args...]{return f(args...);};
    }

    //延迟执行,将结果放到Optional中缓存起来,下次不用重新计算就可以直接返回结果
    T& value()
    {
        if(! m_value.isInit()){
            m_value = m_func();
        }

        return *m_value;
    }

    bool isValueCreated() const
    {
        return m_value.isInit();
    }
};

//辅助函数,简化Lazy的调用
template<class Func, typename... Args>
Lazy<typename std::result_of<Func(Args...)>::type>   //返回值类型Lazy<T>
lazy(Func&& fun, Args&&... args)
{
    using ret_type_t = typename std::result_of<Func(Args...)>::type;
    return Lazy<ret_type_t>(std::forward<Func>(fun), std::forward<Args>(args)...);
}

#endif // _LAZY_H_

//main.cpp

#include "Lazy.hpp"
#include <iostream>
#include <memory>  //for std::shared_ptr

using namespace std;

struct BigObject
{
    BigObject()
    {
        cout << "lazy load big object..." << endl;
    }
};

struct Test
{
private:
    Lazy<std::shared_ptr<BigObject>> m_obj;
public:
    Test()
    {
        m_obj = lazy([]{return std::make_shared<BigObject>();});
    }

    void load()
    {
        m_obj.value();
    }
};

int Foo(int x)
{
    return x * 2;
}

void TestLazy()
{
    //带参数的普通函数
    int a = 4;
    auto lazy1 = lazy(Foo, a);
    cout << lazy1.value() << endl;  //8

    //不带参数的lambda表达式
    Lazy<int> lazy2 = lazy([]{return 12;});
    cout << lazy2.value() << endl;   //12

    //带参的function
    std::function<int(int)> f = [](int x){return x + 3;};
    auto lazy3 = lazy(f, a);
    cout << lazy3.value() << endl;  //7

    //延迟加载大对象
    Test t;
    t.load();  //lazy load big object...
}

int main()
{
    TestLazy();
    return 0;
}
时间: 2024-10-13 05:31:48

第23课 可变参数模板(4)_Optional和Lazy类的实现的相关文章

第21课 可变参数模板(2)_展开参数包

1. 可变参数模板函数 (1)递归函数方式展开参数包 ①一般需要提供前向声明.一个参数包的展开函数和一个递归终止函数. ②前向声明有时可省略,递归终止函数可以是0个或n个参数 (2)逗号表达式和初始化列表方式展开参数包 ①逗号表达式按顺序执行,返回最后一个表达式的值. ②initilizer_list可接受任意多个不同类型的参数. ③借助逗号表达式来展开包,并将返回的结果用于初始化initilizer_list. [编程实验]展开可变参数模板函数的参数包 #include <iostream>

第20课 可变参数模板(1)_模板参数包和函数参数包

1.  参数包(parameter pack) (1)模板参数包(以tuple为例):template<typename- Elements>class tuple ①Elements标识符的左侧使用了省略号,在C++11中Elements被称为"模板参数包",表示可以接受任意多个参数作为模板参数. ②编译器将多个模板参数打包成"单个"的模板参数包,如tuple<int, char, double>实例化模板类时,Element就是包含int

C++ 11 可变参数模板和 boost::any 实现可变参数函数

1 class SqlHelper 2 { 3 public: 4 template <typename... Params> 5 static bool preparedExecute(sql::PreparedStatement* pstmt, Params... parameters) 6 { 7 return doPreparedExecute(pstmt, 1, parameters...); 8 } 9 10 private: 11 template <typename...

可变参数模板用法

//可变参数模板 //可变参数模板,可以创建可接受可变数量参数的模板函数和模板类 //本程序通过模板函数来实例一下可变参数模板的基本用法 #include<iostream> using namespace std; void one(){}//当最后一个参数传完后,需要一个无参的重载版本 template <typename T>//当只剩最后一个参数时,编译器优先选择此模板,这样最后一个输出后面就没有逗号了 void one(T v) { cout << v <

c++11可变参数模板的使用1

1.概述 C++11的新特性--可变模版参数(variadic templates)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数.任意类型的参数.相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进.然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以它也是C++11中最难理解和掌握的特性之一. 虽然掌握可变模版参数有一定难度,但是它却是C++11中最有意思的一个特性,本文希望带领读者由浅入深的认识和掌握这一

c++11 可变参数模板函数

c++11 可变参数模板函数 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector> #include <map> // 在C++11之前,类模板和函数模板只能含有固定数量的模板参数.C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板. // 可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变

C++反射机制:可变参数模板实现C++反射

1. 概要   本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在coding.net的仓库地址.   C++11的新特性--可变模版参数(variadic templates)是C++11新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数.任意类型的参数.关于可变参数模板的原理和应用不是本文重点,不过通过本文中的例子也可充分了解可变参数模板是如何应用的.

数据--第23课 - 队列的优化实现

第23课 - 队列的优化实现 1. 顺序队列的瓶颈 顺序队列 线性表的第一个元素作为队头. 线性表的最后一个元素作为队尾. 入队的新元素是线性表的最后,时间复杂度为O(1). 出队列时需要将后续的所有元素向前移动,时间复杂度是O(n). 2. 顺序队列的优化方案 (1)定义front使其始终代表队头的下标. 出队列时对头元素返回,且front++ (2)定义rear使其始终代表队尾下一个元素的下标. 出队时将新的元素插入,且rear++ (3)没有必要只将下标为0的位置定义为队头. (4)顺序队

C++--第23课 - STL简介

第23课 - STL简介 1. 标准模板SLT C++的作用就是提高程序书写的效率,那么就得代码复用. STL,即:Standard Template(样板) Library,是C++的一部分(常用的数据结构).STL是常用数据结构和算法的集合.STL的目标是标准化组件,提高开发效率和程序可靠性. STL库作为为C++的一部分与编译器一同被发布.STL主要由以下3个部分组成: 容器(Container):管理数据的集合 算法(Algorithm):处理集合内的元素 迭代器(Iterator):遍