小问题也会影响设计的思路,某个问题或某种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 ctoroperator=(_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;45 int main()6 {7 class Person{8 private:9 int _age;10 int _height;11 public:12 Person(){cout<<"default ctor"<<endl;}1314 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; }1819 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 }2728 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 }
3334 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 }4445 void print(){46 cout<<"age:"<<_age<<" height:"<<_height<<endl;47 }48 };49 any a1{ Person(1,2) }; //call Person's move ctor50 any a2(a1); //call Person's copy ctor51 cout<<"----------------"<<endl;5253 Person p = Person(1,2);54 any a3(p); //call Person's copy ctor55 cout<<"----------------"<<endl;5657 any a4(std::in_place_type<Person>, 3, 4); //call Person's parameterized ctor58 Person& p4 = std::any_cast<Person&>(a4); //ref, no copy of Person59 p4.print();6061 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>explicitany(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源代码里的改动恢复回来。