std::any 没有 RTTI,它是如何工作的?

std::any without RTTI, how does it work?

如果我想使用 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如何存储类型信息呢?如我所见,如果我使用 "wrong" 类型调用 std::any_cast,我会按预期得到 std::bad_any_cast 异常。

这是如何实现的,或者这可能只是一个 gcc 功能?

我发现 boost::any 也不需要 RTTI,但我发现也不是如何解决的。 .

深入研究 STL header 本身没有给我任何答案。该代码对我来说几乎不可读。

手动实现有限的 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.

实现的 none 静态数据成员

可能的解决方案之一是为可能存储在 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 来快速检查类型。

注意函数内部静态变量和静态函数的使用。这样做是为了避免静态变量初始化顺序未定义的问题。

TL;DR; std::any 持有指向模板化 class 的静态成员函数的指针。此函数可以执行许多操作并且特定于给定类型,因为函数的实际实例取决于 class.

的模板参数

libstdc++中std::any的实现并不复杂,大家可以看看:

https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/any

基本上,std::any 包含两件事:

  • 指向(动态)分配存储的指针;
  • 指向 "storage manager function" 的指针:
void (*_M_manager)(_Op, const any*, _Arg*);

当您使用类型为 T 的对象构造或分配新的 std::any 时,_M_manager 指向特定于类型 T 的函数(实际上是class 的静态成员函数特定于 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).


class _Manager<_Tp> 实际上是 _Manager_internal<_Tp>_Manager_external<_Tp> 的别名,具体取决于 _Tp。 此 class 还用于 std::any class.

的对象分配/构造