如果我想使用std::any
,可以在RTTI关闭的情况下使用它。以下示例也可以与-fno-rtti
和gcc一起编译并运行。
int main()
{
std::any x;
x=9.9;
std::cout << std::any_cast<double>(x) << std::endl;
}
但是std::any
如何存储类型信息?如我所见,如果我以“错误”类型调用std::any_cast
,则会如预期的那样发生std::bad_any_cast
异常。
这是怎么实现的,或者这可能只是gcc功能?
我发现boost::any
也不需要RTTI,但是我也没有找到解决方法。 Does boost::any need RTTI?。
挖掘STL标头本身并没有给出答案。该代码对我几乎是不可读的。
答案 0 :(得分:28)
TL; DR; std::any
持有指向模板化类的静态成员函数的指针。该函数可以执行许多操作,并且特定于给定类型,因为该函数的实际实例取决于类的模板参数。
在libstdc ++中std::any
的实现并不那么复杂,您可以看一下:
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/any
std::any
基本上有两件事:
void (*_M_manager)(_Op, const any*, _Arg*);
当您使用对象std::any
构造或分配新的T
时,_M_manager
指向特定于类型T
的函数(实际上是静态的) T
特有的类的成员函数:
template <typename _ValueType,
typename _Tp = _Decay<_ValueType>,
typename _Mgr = _Manager<_Tp>, // <-- Class specific to T.
__any_constructible_t<_Tp, _ValueType&&> = true,
enable_if_t<!__is_in_place_type<_Tp>::value, bool> = true>
any(_ValueType&& __value)
: _M_manager(&_Mgr::_S_manage) { /* ... */ }
由于此函数特定于给定类型,因此您不需要RTTI即可执行std::any
所需的操作。
此外,在std::any_cast
中很容易检查自己是否转换为正确的类型。这是std::any_cast
的gcc实现的核心:
template<typename _Tp>
void* __any_caster(const any* __any) {
if constexpr (is_copy_constructible_v<decay_t<_Tp>>) {
if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage) {
any::_Arg __arg;
__any->_M_manager(any::_Op_access, __any, &__arg);
return __arg._M_obj;
}
}
return nullptr;
}
您会看到,这只是对您要强制转换的对象(_any->_M_manager
中的存储函数和要强制转换为类型(&any::_Manager<decay_t<_Tp>>::_S_manage
的管理器函数之间的相等性检查)。
取决于_Manager<_Tp>
,类_Manager_internal<_Tp>
实际上是_Manager_external<_Tp>
或_Tp
的别名。
此类也用于std::any
类的对象分配/构造。
答案 1 :(得分:2)
一种可能的解决方案是为可能存储在any
中的每种类型生成唯一的ID(我想您将更加了解any
在内部的工作方式)。可以做到的代码可能看起来像这样:
struct id_gen{
static int &i(){
static int i = 0;
return i;
}
template<class T>
struct gen{
static int id() {
static int id = i()++;
return id;
}
};
};
实施此操作后,您可以使用类型的ID代替RTTI typeinfo
来快速检查类型。
注意在函数和静态函数中使用静态变量。这样做是为了避免静态变量初始化的顺序不确定。
答案 2 :(得分:-1)
手动实施有限的RTTI并不难。您将需要静态泛型函数。我有很多话可以说,而无需提供完整的实现。 这是一种可能性:
class meta{
static auto id(){
static std::atomic<std::size_t> nextid{};
return ++nextid;//globally unique
};
std::size_t mid=0;//per instance type id
public:
template<typename T>
meta(T&&){
static const std::size_t tid{id()};//classwide unique
mid=tid;
};
meta(meta const&)=default;
meta(meta&&)=default;
meta():mid{}{};
template<typename T>
auto is_a(T&& obj){return mid==meta{obj}.mid;};
};
这是我的第一次观察;远非理想,缺少许多细节。一个人可能会使用meta
的一个实例作为他假设的std::any
实现的非静态数据成员。