1. 程式人生 > >第23課 可變參數模板(4)_Optional和Lazy類的實現

第23課 可變參數模板(4)_Optional和Lazy類的實現

opera -s 緩沖區 data emp 銷毀 由於 方便 lazy load

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;
}

第23課 可變參數模板(4)_Optional和Lazy類的實現