• 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

  • 相关阅读:
    Linux编程make命令(转)
    如何处理Global symbol * requires explicit package name编译错误,以及use strict用法
    6235与旧版本的区别
    Linux下动态链接库的使用
    MTK Socket学习——HTTP请求
    指针和指针的引用
    VC/MFC Combo Box控件的用法
    makefile教程
    常用数据类型使用转换详解
    Linux发送与接收信息
  • 原文地址:https://www.cnblogs.com/fortunely/p/16271717.html
Copyright © 2020-2023  润新知