
blackstar
V1
2022/07/15阅读:43主题:默认主题
any类型转换之谜
背景
struct A{
A(int a){
this->a = a;
}
int a;
};
namespace Test{
struct A{
A(int a){
this->a = a;
}
int a;
};
}
void AnyCast(std::any& param){
const auto value = std::any_cast<Test::A>(param).a;
std::cerr << "value : " << value << std::endl;
}
int main(){
A a = 1;
auto param = std::make_any<A>(a);
AnyCast(param);
return 0;
}
-
上面转换是否成功?
疑问1
为什么自定义内存结构完全相同的同一类型名,仅仅因为命名空间不同就会导致转换异常?去除命名空间是否正常? 为了保证编译通过,需要将自定义类型在不同模块分开。
// module.h
#pragma once
#include <memory>
#include <any>
class ModuleImpl;
class Module{
public:
Module();
void AnyCast(std::any& param);
private:
std::shared_ptr<ModuleImpl> _impl;
};
// module.cpp
#include "module.h"
#include <iostream>
struct A {
A(int a) {
this->a = a;
}
int a;
};
class ModuleImpl {
public:
void AynyCast(std::any& param)
{
const auto value = std::any_cast<A>(param).a;
std::cerr << "value : " << value << "\n";
}
};
Module::Module()
: _impl(std::make_shared<ModuleImpl>())
{
}
void Module::AnyCast(std::any& param)
{
_impl->AynyCast(param);
}
-
上面转换还会出现异常吗?
疑问2
是不是分开模块,加命名空间也不会有问题?针对这个疑问,不是不可能,验证下:
有种时曾相识的感觉。
揭开any_cast 的面纱
这里以MSVC 版本实现的 any_cast 为例。首先我们看下它的实现逻辑:
template <class _Ty>
_NODISCARD remove_cv_t<_Ty> any_cast(any& _Any) {
static_assert(is_constructible_v<remove_cv_t<_Ty>, _Remove_cvref_t<_Ty>&>,
"any_cast<T>(any&) requires remove_cv_t<T> to be constructible from remove_cv_t<remove_reference_t<T>>&");
const auto _Ptr = _STD any_cast<_Remove_cvref_t<_Ty>>(&_Any);
if (!_Ptr) {
_Throw_bad_any_cast();
}
return static_cast<remove_cv_t<_Ty>>(*_Ptr);
}
关键函数:any_cast<_Remove_cvref_t<_Ty>>(&_Any)
template <class _ValueType>
_NODISCARD _ValueType* any_cast(any* const _Any) noexcept {
// retrieve a pointer to the _ValueType contained in _Any, or null
static_assert(!is_void_v<_ValueType>, "std::any cannot contain void.");
if constexpr (is_function_v<_ValueType> || is_array_v<_ValueType>) {
return nullptr;
} else {
if (!_Any) {
return nullptr;
}
return _Any->_Cast<_Remove_cvref_t<_ValueType>>();
}
}
关键函数:_Any->_Cast<_Remove_cvref_t<_ValueType>>()
template <class _Decayed>
_NODISCARD _Decayed* _Cast() noexcept { // if *this contains a value of type _Decayed, return a pointer to it
return const_cast<_Decayed*>(static_cast<const any*>(this)->_Cast<_Decayed>());
}
关键函数:static_cast<const any>(this)->_Cast<_Decayed>()*
template <class _Decayed>
_NODISCARD const _Decayed* _Cast() const noexcept {
// if *this contains a value of type _Decayed, return a pointer to it
const type_info* const _Info = _TypeInfo();
if (!_Info || *_Info != typeid(_Decayed)) {
return nullptr;
}
if constexpr (_Any_is_trivial<_Decayed>) {
// get a pointer to the contained _Trivial value of type _Decayed
return reinterpret_cast<const _Decayed*>(&_Storage._TrivialData);
} else if constexpr (_Any_is_small<_Decayed>) {
// get a pointer to the contained _Small value of type _Decayed
return reinterpret_cast<const _Decayed*>(&_Storage._SmallStorage._Data);
} else {
// get a pointer to the contained _Big value of type _Decayed
return static_cast<const _Decayed*>(_Storage._BigStorage._Ptr);
}
}
本次问题的关键代码:
// if *this contains a value of type _Decayed, return a pointer to it
const type_info* const _Info = _TypeInfo();
if (!_Info || *_Info != typeid(_Decayed)) {
return nullptr;
}
可以用一行代码说明:
typeid(A) == typeid(Test::A)
成立吗?
废话不多说,直接开干:
struct A {
A(int a) {
this->a = a;
}
int a;
};
namespace Test {
struct A {
A(int a) {
this->a = a;
}
int a;
};
}
int main()
{
const auto same = typeid(A) == typeid(Test::A);
std::cerr << "same : " << std::boolalpha << same << std::endl;
return 0;
}
总结
作为一名合(摸)格(鱼)的软件开发工程师,最后总结一下不过分吧。
-
在使用any类型转换的时候,一定要保证类型来源唯一 -
不同模块之间不要随便拷贝代码 -
只有代码才能解决代码中的BUG
本次 any类型转换之谜完结撒花!!!
作者介绍

blackstar
V1
一个常年混迹于摸鱼网站的码农