小問題也會影響設計的思路,某個問題或某種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源代碼里的改動恢復回來。