std ::任何没有RTTI的东西,它如何工作?

时间:2018-07-16 12:13:58

标签: c++ stl c++17 rtti

如果我想使用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标头本身并没有给出答案。该代码对我几乎是不可读的。

3 个答案:

答案 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实现的非静态数据成员。