Modern C++ std::any为何要求Tp可拷贝构造?

小问题也会影响设计的思路,某个问题或某种case的探讨有助于理解设计的初衷。

声明:以下_Tp/Tp都是指要放入std::any的对象的类型。

它要求_Tp is_copy_constructible, 仅仅是因为有很多函数的实现调用了Tp的拷贝构造函数吗?比如说上节提到的初始化函数:

any(_Tp&& __value) //调用_Tp copy ctor/move ctor
any(in_place_type_t<_Tp>, _Args&&... __args)  //调用_Tp parameterized ctor
any(in_place_type_t<_Tp>, initializer_list<_Up> __il, _Args&&... __args) //调用_Tp parameterized ctor
 
operator=(_Tp&& __rhs) //先构造临时any对象(调用第一种情况),再move给*this

第一种case:如果__value是左值,则最终会调用到_Tp copy ctor:

_Manager_internal 
	template<typename _Up>
	  static void
	  _S_create(_Storage& __storage, _Up&& __value)
	  {
	    void* __addr = &__storage._M_buffer;
	    ::new (__addr) _Tp(std::forward<_Up>(__value));
	  }

_Manager_external 
	template<typename _Up>
	  static void
	  _S_create(_Storage& __storage, _Up&& __value)
	  {
	    __storage._M_ptr = new _Tp(std::forward<_Up>(__value));
	  }

简单写个例子,证实我们的推断:

  1 #include <any>
  2 #include <iostream>
  3 using namespace std;
  4
  5 int main()
  6 {
  7     class Person{
  8         private:
  9             int _age;
 10             int _height;
 11         public:
 12             Person(){cout<<"default ctor"<<endl;}
 13
 14             Person(int age,int h):_age(age),_height(h){
 15                 cout<<"parameterized ctor"<<endl;
 16             }
 17             Person(const Person& o): _age(o._age), _height(o._height){ cout<<"copy ctor"<<endl; }
 18
 19             Person& operator=(const Person& o){
 20                 if (this != &o) {
 21                     _age = o._age;
 22                     _height = o._height;
 23                     cout<<"assignment"<<endl;
 24                 }
 25                 return *this;
 26             }
 27
 28             Person(Person&& o) noexcept : _age(std::move(o._age)), _height(std::move(o._height)) {
 29                 o._age = 0;
 30                 o._height = 0;
 31                 std::cout << "move ctor" << std::endl;
 32             }
33
 34             Person& operator=(Person&& o) noexcept {
 35                 if (this != &o) {
 36                     _age = std::move(o._age);
 37                     _height = std::move(o._height);
 38                     o._age = 0;
 39                     o._height = 0;
 40                     std::cout << "move assignment" << std::endl;
 41                 }
 42                 return *this;
 43             }
 44
 45             void print(){
 46                 cout<<"age:"<<_age<<" height:"<<_height<<endl;
 47             }
 48     };
 49     any a1{ Person(1,2) }; //call Person's move ctor
 50     any a2(a1);            //call Person's copy ctor
 51     cout<<"----------------"<<endl;
 52
 53     Person p = Person(1,2);
 54     any a3(p);             //call Person's copy ctor
 55     cout<<"----------------"<<endl;
 56
 57     any a4(std::in_place_type<Person>, 3, 4); //call Person's parameterized ctor
 58     Person& p4 = std::any_cast<Person&>(a4);  //ref, no copy of Person
 59     p4.print();
 60
 61     return 0;
 62 }

输出如下:

parameterized ctor
move ctor
copy ctor
----------------
parameterized ctor
copy ctor
----------------
parameterized ctor
age:3 height:4
 

 50、54行都调用了Person's copy ctor.

50行:any copy

54行:由一个左值Person对象初始化一个any对象

但是不是所有暴露出来的接口都需要Tp支持copy constructible哪?是不是不支持就无法完成初始化+获取回来数据 这种有意义的操作哪?显然不是,请看57-59行。

57行:调用Tp的parameterized ctor构造了一个any对象,实际没调用Tp的copy ctor, 但实现中依然要求Tp是copy constructible的, 如下所示:

    template <typename _Tp, typename... _Args, typename _VTp = decay_t<_Tp>,
	      typename _Mgr = _Manager<_VTp>,
	      __any_constructible_t<_VTp, _Args&&...> = false>
      explicit
      any(in_place_type_t<_Tp>, _Args&&... __args)
      : _M_manager(&_Mgr::_S_manage)
      {
	_Mgr::_S_create(_M_storage, std::forward<_Args>(__args)...);
      }



    template <typename _Res, typename _Tp, typename... _Args>
      using __any_constructible
	= enable_if<__and_<is_copy_constructible<_Tp>,
			   is_constructible<_Tp, _Args...>>::value,
		    _Res>;

    template <typename _Tp, typename... _Args>
      using __any_constructible_t
	= typename __any_constructible<bool, _Tp, _Args...>::type;

看到enable_if<__and_<is_copy_constructible<_Tp>没?正是这厮!

不相信的话,我们做个试验:把我们上面例子中的第17行改为copy ctor deleted. 删除49~56行

Person(const Person& o)=delete;

编译报错!

 好了,我们修改一下any源代码,把198行注释掉以期盼甩掉对Tp copy ctor的限制:

196     template <typename _Tp, typename... _Args, typename _VTp = decay_t<_Tp>,
197               typename _Mgr = _Manager<_VTp>>
198               //__any_constructible_t<_VTp, _Args&&...> = false>
199       explicit
200       any(in_place_type_t<_Tp>, _Args&&... __args)
201       : _M_manager(&_Mgr::_S_manage)
202       {
203         _Mgr::_S_create(_M_storage, std::forward<_Args>(__args)...);
204       }

编译竟然又报错:

明明调用不到Tp的copy ctor啊, 为何报错???

让我们仔细看下589行:

573     any::_Manager_internal<_Tp>::
574     _S_manage(_Op __which, const any* __any, _Arg* __arg)
575     {
576       // The contained object is in _M_storage._M_buffer
577       auto __ptr = reinterpret_cast<const _Tp*>(&__any->_M_storage._M_buffer);
578       switch (__which)
579       {
580       case _Op_access:
581         __arg->_M_obj = const_cast<_Tp*>(__ptr);
582         break;
583       case _Op_get_type_info:
584 #if __cpp_rtti
585         __arg->_M_typeinfo = &typeid(_Tp);
586 #endif
587         break;
588       case _Op_clone:
589         ::new(&__arg->_M_any->_M_storage._M_buffer) _Tp(*__ptr);

 虽然运行时没用到,但是编译依然要编译这一行,编译::new ... Person(const Person&)肯定报错啊,因为没有这个函数!这下好了,不管你用到没用到Tp的copy ctor, 为了编译通过,你的Tp必须要有copy ctor了,躲不过了!

_Manager_external也一样躲不过:

622       case _Op_clone:
623     __arg->_M_any->_M_storage._M_ptr = new _Tp(*__ptr);
624     __arg->_M_any->_M_manager = __any->_M_manager;

最后,不要忘了把any源代码里的改动恢复回来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深山老宅

鸡蛋不错的话,要不要激励下母鸡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值