C++消息总线Mozart:optional

optional原理

Mozart的optional也是一个容器,负责管理一个可选的容纳值,这个容纳值可以存在,也可以不存在。文件git地址:https://github.com/libmozart/foundation/blob/master/mozart%2B%2B/mpp_foundation/optional.hpp

其实现原理:optional所持有的数据除容纳值外,还专门添加布尔标志,标记当前optional是否真是存在。因此,optional大小通常比包含的对象大一个字节,参见optional的内部数据结构定义:

std::array<unsigned char, 1 + sizeof(T)> _memory{0};

第一个字节memory[0]存储容纳值的状态;如果memory[0]==true,表示optional存在一个数据对象,memory[0] == false表示不存在一个数据对象。

第一个字节以外的其他部分memory[1, …, 1+sizeof(T)]存储的是原始数据对象。

optional构造

optional的构造函数包括默认构造函数,空构造函数,隐式(移动)构造,列表初始化构造,多实参初始化构造等多个形式。

默认构造

optional() = default;

optional空构造

optional空构造仅用于空对象的生成,optional空对象仅仅是一个标识说明,表示当前optional未包含任何对象。

// 定义一个optional空对象
struct optional_none_t {};
static constexpr optional_none_t none {};

optional于此对应的空构造函数为:

optional(optional_none_t) : optional() 
{}

optional隐式构造

隐式构造实现基于原始对象生成optional对象,隐式构造包括两种:一种为基于左值的隐式构造,一种为基于右值的移动隐式构造。

基于左值的隐式构造

optional(const T &t) 
{
    new(_memory.data() + 1) T(t);
    _memory[0] = static_cast<unsigned char>(true);
}

基于右值的移动隐式构造

optional(T &&t) 
{
    new(_memory.data() + 1) T(std::forward<T>(t));
    _memory[0] = static_cast<unsigned char>(true);
}

option拷贝构造

optional的拷贝构造包括左值拷贝构造和右值拷贝构造两种。

左值拷贝

左值拷贝构造为传统意义上C++98支持的拷贝构造方式。定义如下:

optional(const optional<T> &other)
{
    if (other.has_value())
    {
        new(_memory.data() + 1) T(other.get());
        _memory[0] = static_cast<unsigned char>(true);
    }
}

右值拷贝

右值拷贝为C++11支持的基于移动语义的拷贝构造,定位如下:

// 实现当前对象_memory和other的_memory数据交换
void swap(optional<T> &&other) 
{
    std::swap(this->_memory, other._memory);
}

optional(optional<T> &&other) noexcept 
{
    swap(other);
}

optional转发构造

optional的其他构造方法,都采用some中介转发实现。我们首先介绍some的in_place匹配引导机制,然后介绍some中介转发构造机制。

in_place

in_place机制其实就是一种函数定义匹配机制。

#define mpp_in_place_type_t(T)  mpp_impl::in_place_t(&)(mpp_impl::in_place_type_tag<T>)

template <typename T>
struct in_place_type_tag 
{
};

struct in_place_t 
{
};

宏mpp_in_place_type_t就是一个函数声明定义,这个函数入参为mpp_impl::in_place_type_tag,返回值为in_place_t。

template <typename T>
inline in_place_t in_place(in_place_type_tag<T> = in_place_type_tag<T>()) 
{
    return in_place_t{};
}

inline in_place_t in_place(in_place_type_tag = in_place_type_tag()) 函数定义就是满足宏mpp_in_place_type_t声明函数的一个匹配实现。

some转发机制

简单的说some转发就是通过optional模块提供的some函数实现optional对象构造,optional模块提供的some函数包括三种,他们分别是单对象some转发,入参列表形式转发,列表初始化转发。

单对象some

optional<typename std::decay::type> some(T &&v)实现单对象右值赋值构造初始化工作。

template <typename T>
constexpr optional<typename std::decay<T>::type> some(T &&v)
{
    return optional<typename std::decay<T>::type>(std::forward<T>(v));
}

调用方式为:

auto optionalInt = mpp::some(int(1));

这种定义初始化就会调用此some转发初始化构造函数。

入参列表形式some

此转发构造方式赋值适配c++98标准老式初始化方式,调用方式为:

auto optionalVect98 = mpp::some<std::vector<int>>(1, 2, 3, 4, 5)));

some转发构造函数定义:

template <typename T, typename... Args>
constexpr optional<T> some(Args &&... args)
{
	return optional<T>(mpp_impl::in_place, std::forward<Args>(args)...);
}

mpp_impl::in_place是一个函数对象,通过此函数对象,编译器会匹配到optional的optional(mpp_in_place_type_t(T), Args &&… args)构造函数。

template <typename... Args>
constexpr explicit optional(mpp_in_place_type_t(T), Args &&... args)
: optional(T{std::forward<Args>(args)...})
{}
列表初始化some

此转发构造方式赋值适配c++11标准新式初始化方式,调用方式为:

auto optionalVect11  = mpp::some<decltype(v)>({ 1, 2, 3, 4, 5 }));

some转发构造函数定义:

template <typename T, typename U, typename... Args>
constexpr optional<T> some(std::initializer_list<U> il, Args &&... args)
{
    return optional<T>(mpp_impl::in_place, il, std::forward<Args>(args)...);
}

mpp_impl::in_place是一个函数对象,通过此函数对象,编译器会匹配到optional的optional(mpp_in_place_type_t(T), std::initializer_list il, Args &&… args)构造函数。

template <typename U, typename... Args>
constexpr explicit optional(mpp_in_place_type_t(T), std::initializer_list<U> il, Args &&... args)
: optional(T{il, std::forward<Args>(args)...}) 
{}

optional析构

optional析构实现optional数据的释放和回收,通过定位析构实现对象所管理资源的回收。

T *ptr() 
{
    return has_value() ? reinterpret_cast<T *>(_memory.data() + 1) : nullptr;
}

const T *ptr() const
{
    return has_value() ? reinterpret_cast<const T *>(_memory.data() + 1) : nullptr;
}

~optional()
{
    if (has_value()) 
    {
        ptr()->~T();
        _memory[0] = static_cast<unsigned char>(false);
    }
}

析构函数~optional()通过ptr()函数获取所持有对象的指针,然后采用定位析构的方式回收资源。

此处需要重点说明的是:为何采用定位析构,而不是通过delete ptr()这种方式?ptr所指向对象在函数调用栈上,对应地址为_memory.data() + 1,而不在堆heap。所以在这儿的回收,只能回收数据,不能把对象交换操作系统,而定位析构专门是为解决这个问题的,这就是不能直接调用delete ptr()回收资源的原因。

optional赋值

同样赋值也存在两种,分别是左值赋值,右值赋值。

左值赋值

左值赋值通过std::swap数据交换赋值操作。

void swap(optional<T> &other) 
{
    std::swap(this->_memory, other._memory);
}

optional &operator=(const optional<T> &other) 
{
    if (this == &other)
    {
        return *this;
    }

    optional<T> t(other);
    swap(t);
    return *this;
}

右值赋值

右值赋值通过std::swap数据交换赋值操作。

void swap(optional<T> &&other) 
{
    std::swap(this->_memory, other._memory);
}

optional &operator=(optional<T> &&other) noexcept
{
    if (this == &other)
    {
        return *this;
    }

    swap(other);
    return *this;
}

get_or

获取原始get_or,根据调用对象是否为const对象,可分为const版本和分const版本。分别定义如下:

// 非const版本get_or
template <typename U>
std::enable_if_t<std::is_same<T, U>::value, T> &get_or(U &&o)
{
    if (ptr() == nullptr)
    {
        return o;
    }
    return *ptr();
}

// const版本get_or
template <typename U>
const std::enable_if_t<std::is_same<T, U>::value, T> &get_or(U &&o) const
{
    if (ptr() == nullptr) 
    {
        return o;
    }
    return *ptr();
}

std::enable_if_t<std::is_same<T, U>::value, T> 用于限制U和T必须是同一类型的数据,否则会报出编译错误。

apply/apply_or

apply和apply_or与get_or实现方式存在异曲同工之处,大家可以自行分析,这儿贴出其定义:

void apply(const std::function<void(T &)> &consumer)
{
    if (ptr() != nullptr)
    {
        consumer(get());
    }
}

void apply(const std::function<void(const T &)> &consumer) const 
{
    if (ptr() != nullptr) 
    {
        consumer(get());
    }
}

template <typename R>
R apply_or(const R &r, const std::function<R(T &)> &consumer) 
{
    if (ptr() != nullptr) 
    {
        return consumer(get());
    }
    return r;
}

template <typename R>
R apply_or(const R &r, const std::function<R(const T &)> &consumer) const 
{
    if (ptr() != nullptr) 
    {
        return consumer(get());
    }
    return r;
}

总结

本文详细介绍了,optional类的数据存储和接口。希望可以对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值