当从实际存储类型到请求类型的隐式转换可能时,为什么std::any_cast抛出std::bad_any_cast异常?
例如:
std::any a = 10; // holds an int now
auto b = std::any_cast<long>(a); // throws bad_any_cast exception
Run Code Online (Sandbox Code Playgroud)
为什么这是不允许的,是否有允许隐式转换的解决方法(如果std::any保存的确切类型未知)?
Sto*_*ica 44
std::any_cast以...表示typeid.引用cppreference:
std::bad_any_cast如果typeid请求ValueType的内容与操作数的内容不匹配则抛出.
既然typeid不允许实现"弄明白"隐式转换是可能的,那么(据我所知)也any_cast无法知道它是否可能.
换句话说,提供的类型擦除std::any依赖于仅在运行时可用的信息.而且这些信息并不像编译器用于计算转换的信息那么丰富.这就是C++ 17中类型擦除的代价.
Yak*_*ont 19
要做你想做的事,你需要完整的代码反思和具体化.这意味着每种类型的每个细节都必须保存到每个二进制文件(以及每种类型的每个函数的每个签名!并且每个模板都在任何地方!),当你要求从任何类型转换为类型X时,你会通过有关X的数据进入any,其中包含有关其包含的类型的足够信息,基本上尝试将转换编译为X并失败.
有些语言可以做到这一点; 每个二进制文件都带有IR字节码(或原始源)和解释器/编译器.在大多数任务中,这些语言往往比C++慢2倍或更慢,并且具有更大的内存占用量.有可能没有这个成本的那些功能,但没有人拥有我所知道的那种语言.
C++没有这种能力.相反,它在编译期间忘记了几乎所有关于类型的事实.对于任何一个,它会记住一个typeid,它可用于获得完全匹配,以及如何将其存储转换为所述完全匹配.
std::any 必须使用类型擦除来实现。那是因为它可以存储任何类型,不能是模板。目前 C++ 中没有其他功能可以实现这一点。
这也就意味着,std::any将存储类型擦除指针,void*并且std::any_cast将这个指针转换为指定的类型,就是这样。它只是使用typeidbefore 进行完整性检查,以检查您将其转换为的类型是否是存储在 any 中的类型。
使用当前的实现是不可能允许隐式转换的。考虑一下(暂时忽略typeid检查)。
std::any_cast<long>(a);
Run Code Online (Sandbox Code Playgroud)
a存储一个int而不是一个long。应该std::any怎么知道?它可以将其void*强制转换为指定的类型,取消引用并返回它。将指针从一种类型转换为另一种类型是严格的别名违规并导致 UB,所以这是一个坏主意。
std::any将不得不存储存储在其中的对象的实际类型,这是不可能的。你现在不能在 C++ 中存储类型。它可以维护一个类型列表以及它们各自的typeids 并切换它们以获取当前类型并执行隐式转换。但是对于您将要使用的每一种类型,都没有办法做到这一点。用户定义的类型无论如何都不起作用,您必须依靠宏等内容来“注册”您的类型并为其生成适当的切换案例1。
也许是这样的:
template<typename T>
T any_cast(const any &Any) {
const auto Typeid = Any.typeid();
if (Typeid == typeid(int))
return *static_cast<int *>(Any.ptr());
else if (Typeid == typeid(long))
return *static_cast<long *>(Any.ptr());
// and so on. Add your macro magic here.
// What should happen if a type is not registered?
}
Run Code Online (Sandbox Code Playgroud)
这是一个很好的解决方案吗?不,到目前为止。转换成本很高,而且 C++ 的口头禅是“不用为不使用的东西付费”,所以不,目前没有办法实现这一点。该方法也是“hacky”且非常脆弱(如果您忘记注册类型会发生什么)。简而言之,做这样的事情可能带来的好处首先不值得麻烦。
是否有允许隐式转换的解决方法(以防 std::any 保存的确切类型未知)?
是的,使用上面提到的宏寄存器方法实现std::any(或类似的类型)和std::any_cast你自己1。不过我不会推荐它。如果您不知道也不能知道std::any存储什么类型并需要访问它,那么您可能存在设计缺陷。
1 : 实际上不知道这是否可能,我不太擅长宏 (ab) 使用。您还可以为您的自定义实现硬编码您的类型或使用单独的工具。
| 归档时间: |
|
| 查看次数: |
3472 次 |
| 最近记录: |