boost::any浅析

boost::any简介

C语言中有void*指针用于存放、传递任意类型数据,C++中呢?
boost库就提供了这样一个类:boost::any,一个很短小的类,主要作用是定义一个变量存放任意类型的数据。

boost::any用法

可以在存储的时候,将要存储的对象类型转换为boost::any类型,而要使用的时候,用boost::any_cast转型为相应的类型。
注意:转型的时候,只能转型为存储时的类型或者能隐式转换的类型,如果转换为不能兼容的类型,可能会导致未定义行为。

#include <boost/any.hpp>
#include <iostream>
#include <list>
#include <vector>
using namespace std;

int main()
{
    vector<boost::any> vec;
    list<int> lst = {4,5,6};
    vec.emplace_back(1);
    vec.emplace_back(string("hello"));
    vec.emplace_back(lst);

    int d = boost::any_cast<int>(vec[0]); // 将vec[0]转型为int
    cout << d << endl;

    const string &s = boost::any_cast<string&>(vec[1]); // 将vec[1]转型为string&
    cout << s << endl;

    const list<int>& tmpLst = boost::any_cast<list<int>&>(vec[2]); // 将vec[2转型为list<int>&
    for (auto const& e : tmpLst) {
        cout << e << ' ';
    }
    cout << endl;
    return 0;
}

当我们不确定存储的any是何种类型时,可以利用RTTI技术typeid操作符进行判断

#include <boost/any.hpp>
#include <iostream>
#include <list>
#include <vector>
using namespace std;

int main()
{
    vector<boost::any> vec;
    list<int> lst = {4,5,6};
    vec.emplace_back(1);
    vec.emplace_back(string("hello"));
    vec.emplace_back(lst);

    vector<boost::any>::iterator it; // vector<boost::any>迭代器
    boost::any anyone;
    for (it = vec.begin(); it != vec.end(); ++it) {
        anyone = *it;

        if (anyone.type() == typeid(int)) { // typeid获取指定类型type_info类信息
            cout << boost::any_cast<int>(anyone) << endl;
        }
        else if(anyone.type() == typeid(string)) {
            cout << boost::any_cast<string>(anyone) << endl;
        }
        else {
            cout << "unknow type" << endl;
        }
    }
    return 0;
}

boost:any源码

实现any功能主要由三个部分组成:
1)any类;
2)内部类holder保存数据,其基类placeholder声明接口;
3)获取数据时,通过any_cast转型;

any 类及内部类holder

any class中实际存储用户实参的是指针成员content,content实际指向的是holder类型对象,而holder是一个内部类,其基类placeholder。

placeholder主要定义2个接口,用于帮助any获取content所指向的对象类型信息,以及深度克隆any成员content所指对象。
holder实现placeholder接口,非常重要一点是内部持有held,通过传入any实参拷贝构造或移动构造而来。也就是说,如果传入any的是右值,实参对象会移动构造held;如果传入any的是左值,实参对象会拷贝构造held。

注意:
1)传给any的实参必须是对象(或基本类型)本身,可以是左值,也可以是右值,但不能是地址,除非要通过any保存的是指针本身。

class any{    public: // structors
        // 一组构造函数
        any() BOOST_NOEXCEPT
          : content(0)
        {
        }

        template<typename ValueType>
        any(const ValueType & value)
          : content(new holder<ValueType>(value)) // 构造一个holder对象, 并由content接管
        {
        }

        any(const any & other)
          : content(other.content ? other.content->clone() : 0) // 拷贝other所指对象, 深度拷贝
        {
        }

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
        // Move constructor
        any(any&& other) BOOST_NOEXCEPT
          : content(other.content)
        {
            other.content = 0;
        }

        // Perfect forwarding of ValueType
        template<typename ValueType>
        any(ValueType&& value, typename boost::disable_if<boost::is_same<any&, ValueType> >::type* = 0)
          : content(new holder< typename remove_reference<ValueType>::type >(static_cast<ValueType&&>(value)))
        {
        }
#endif
        // 析构函数
        ~any() BOOST_NOEXCEPT
        {
            delete content;
        }

    public: // modifiers
        // swap成员函数
        any & swap(any & rhs) BOOST_NOEXCEPT
        {
            std::swap(content, rhs.content);
            return *this;
        }

#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
        template<typename ValueType>
        any & operator=(const ValueType & rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

        any & operator=(any rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

#else
        any & operator=(const any& rhs)
        {
            any(rhs).swap(*this);
            return *this;
        }

        // move assignement
        any & operator=(any&& rhs) BOOST_NOEXCEPT
        {
            rhs.swap(*this);
            any().swap(rhs);
            return *this;
        }

        // Perfect forwarding of ValueType
        template <class ValueType>
        any & operator=(ValueType&& rhs)
        {
            any(static_cast<ValueType&&>(rhs)).swap(*this);
            return *this;
        }
#endif

    public: // queries

        bool empty() const BOOST_NOEXCEPT
        {
            return !content;
        }

        const std::type_info & type() const
        {
            return content ? content->type() : typeid(void);
        }

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
    private: // types
#else
    public: // types (public so any_cast can be non-friend)
#endif

        class placeholder
        {
        public: // structors

            virtual ~placeholder()
            {
            }

        public: // queries
            // 获取类型信息
            virtual const std::type_info & type() const = 0;
            // 克隆对象
            virtual placeholder * clone() const = 0;
        };

        template<typename ValueType>
        class holder : public placeholder
        {
        public: // structors

            holder(const ValueType & value)
              : held(value) // 这里会发生拷贝构造, 通过value拷贝构造held
            {
            }

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
            holder(ValueType&& value)
              : held(static_cast< ValueType&& >(value)) // 这里会发生移动构造, 通过value移动构造held
            {
            }
#endif
        public: // queries
            // 获取ValueType的类型信息, 返回type_info对象
            virtual const std::type_info & type() const
            {
                return typeid(ValueType);
            }
            // 拷贝构造一个holder对象, 返回一个placeholder指针
            virtual placeholder * clone() const
            {
                return new holder(held);
            }

        public: // representation
            ValueType held; // held是对象类型, ValueType是要存储对象的原生类型, 通过模板参数传入

        private: // intentionally left unimplemented
            holder & operator=(const holder &); // 没有实现
        };

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

    private: // representation
        template<typename ValueType>
        friend ValueType * any_cast(any *) BOOST_NOEXCEPT;

        template<typename ValueType>
        friend ValueType * unsafe_any_cast(any *) BOOST_NOEXCEPT;

#else
    public: // representation (public so any_cast can be non-friend)

#endif
        placeholder * content;
    };

转型any_cast

any通过统一的placeholder类型指针content保存传入实参对象,那么当要使用时,如何知道取出并转换成所需类型呢?
boost为any提供转型操作any_cast<>,any_cast可以将any保存的对象转换成指定类型。

any_cast有多个重载版本,

//-----------------------------------------------------
// 一组any_cast函数模板

    // 参数是any*
    template<typename ValueType>
    ValueType * any_cast(any * operand) BOOST_NOEXCEPT
    {
        // 当operand非空, 且所指对象类型名与 模板参数ValueType类型名相同时, 
        // 将operator所指对象用static_cast转型为模板参数对应的指针类型; 否则, 返回空指针
        return operand &&
#ifdef BOOST_AUX_ANY_TYPE_ID_NAME
            std::strcmp(operand->type().name(), typeid(ValueType).name()) == 0
#else
            operand->type() == typeid(ValueType)
#endif
            ? &static_cast<any::holder<ValueType> *>(operand->content)->held
            : 0;
    }

    // 参数是const any*
    template<typename ValueType>
    inline const ValueType * any_cast(const any * operand) BOOST_NOEXCEPT
    {
        return any_cast<ValueType>(const_cast<any *>(operand));
    }

    // 参数是any&, 注意引用不能是空, 否则抛出异常
    // 由于返回值是ValueType, 会发生对operator代表对象的拷贝构造
    template<typename ValueType>
    ValueType any_cast(any & operand)
    {
        // 定义内嵌类型nonref, 可用于去除引用(左值引用, 右值引用)
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;


#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
        // If 'nonref' is still reference type, it means the user has not
        // specialized 'remove_reference'.

        // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro
        // to generate specialization of remove_reference for your class
        // See type traits library documentation for details
        BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif
        // result是operand去除引用的指针类型
        nonref * result = any_cast<nonref>(&operand);
        if(!result)
            boost::throw_exception(bad_any_cast());
        return *result;
    }

    // 参数是const any&
    template<typename ValueType>
    inline ValueType any_cast(const any & operand)
    {
        typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
        // The comment in the above version of 'any_cast' explains when this
        // assert is fired and what to do.
        BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
#endif
        // 将operator转型为const nonref& (这里ValueType是去除const, 引用后的ValueType)
        return any_cast<const nonref &>(const_cast<any &>(operand));
    }

不安全转型unsafe_any_cast

类似于安全转型any_cast,但不做any实际存储对象类的与要转型的模板是否一致的类型验证。不推荐使用。

    // Note: The "unsafe" versions of any_cast are not part of the
    // public interface and may be removed at any time. They are
    // required where we know what type is stored in the any and can't
    // use typeid() comparison, e.g., when our types may travel across
    // different shared libraries.
    template<typename ValueType>
    inline ValueType * unsafe_any_cast(any * operand) BOOST_NOEXCEPT
    {
        return &static_cast<any::holder<ValueType> *>(operand->content)->held;
    }

    template<typename ValueType>
    inline const ValueType * unsafe_any_cast(const any * operand) BOOST_NOEXCEPT
    {
        return unsafe_any_cast<ValueType>(const_cast<any *>(operand));
    }

使用boost::any注意事项

虽说boost::any能存放任意类型,但调用了被存放对象的成员函数。如果被存放对象禁用copy操作,那么就不能直接存放对象本身,因为会调用被存放对象的copy构造函数。

且看下面代码:

#include "muduo/base/noncopyable.h"

#include <boost/any.hpp>
#include <iostream>
#include <memory>

using namespace std;

class Context : muduo::noncopyable // 禁用拷贝
{
public:
	Context(int val)
		: val_(val)
	{}

	void print()
	{
		cout << "val = " << val_;
	}

private:
	int val_;
};

int main()
{
	auto context = new Context(10);
	boost::any any = context;

	auto conn = boost::any_cast<Context>(&any);
	conn->print();
	return 0;
}

Context 类禁用拷贝,如果直接将context通过boost::any any = context;,会调用template<typename ValueType> any & operator=(const ValueType & rhs)

        template<typename ValueType>
        any & operator=(const ValueType & rhs)
        {
            any(rhs).swap(*this); // 这里用rhs构造any,因此会调用any的构造函数
            return *this;
        }

上面代码中,用rhs构造了临时对象any,因此会调用any的构造函数

        template<typename ValueType>
        any(const ValueType & value)
          : content(new holder<ValueType>(value)) // 构造一个holder对象, 并由content接管
        {
        }

注意到这里ValueType类型可以推导出来为Context类型,而new holder<ValueType>(value)会导致调用ValueType即Context的拷贝构造函数,而Context的拷贝构造函数已经禁用,这样做会导致错误,甚至程序崩溃。

解决办法:
将any存放对象由禁用拷贝到Context对象,改为指向Context对象的智能指针shared_ptr,来替代直接存放对象。

int main()
{
	shared_ptr<Context> context(new Context(10)); // 存放对象改为shared_ptr<>,允许拷贝
	boost::any any = context;

	auto conn = boost::any_cast<shared_ptr<Context>>(any);
	conn->print();
	return 0;
}

参考

https://blog.csdn.net/u010216743/article/details/77772078
https://zhuanlan.zhihu.com/p/326781049

posted @ 2022-05-14 22:02  明明1109  阅读(1123)  评论(0编辑  收藏  举报