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

Kla*_*aus 24 c++ stl rtti c++17

如果我想使用std::any我可以使用RTTI关闭.以下示例-fno-rtti使用gcc 编译并按预期运行.

int main()
{   
    std::any x;
    x=9.9;
    std::cout << std::any_cast<double>(x) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

但是如何std::any存储类型信息?正如我所看到的,如果我std::any_cast使用"错误"类型调用,我会得到std::bad_any_cast预期的异常.

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

我发现boost::any也不需要RTTI,但我发现也没有解决这个问题.boost :: any需要RTTI吗?.

深入研究STL标题本身并没有给我答案.那段代码对我来说几乎是不可读的.

Hol*_*olt 32

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


std::anylibstdc ++中的实现并不复杂,您可以查看它:

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

基本上,std::any有两件事:

  • 指向(动态)分配存储的指针;
  • 指向"存储管理器功能"的指针:
void (*_M_manager)(_Op, const any*, _Arg*);
Run Code Online (Sandbox Code Playgroud)

当您std::any使用类型的对象构造或赋值new 时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) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

由于此函数特定于给定类型,因此您无需RTTI即可执行所需的操作std::any.

此外,很容易检查您是否正在投射到正确的类型std::any_cast.以下是gcc实现的核心std::any_cast:

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;
}
Run Code Online (Sandbox Code Playgroud)

您可以看到它只是在您尝试cast(_any->_M_manager)的对象内部存储的函数与要转换为(&any::_Manager<decay_t<_Tp>>::_S_manage)的类型的manager函数之间的相等性检查.


该类_Manager<_Tp>实际上是其中一个_Manager_internal<_Tp>_Manager_external<_Tp>依赖的别名_Tp.该类还用于为类分配/构造对象std::any.

  • 请注意,这取决于链接器和加载器合并函数的多个实例,因此不能跨DLL边界在MinGW上工作(/sf/ask/3170320751/ -mingw) (3认同)
  • 简而言之:它们存储一个指向模板化函数的静态实例的指针,该实例是唯一的,因为该模板的实例取决于给定的类型.我是对的? (2认同)
  • @Klaus 简而言之,是的;) (2认同)
  • 请注意,此构造(指向静态函数模板实例的指针对于每种模板类型都是唯一的)会破坏一些编译器优化,例如MSVC的`/ Gy`与其链接器的`/ OPT:ICF`相结合,请参阅[本页]上的注释](https://msdn.microsoft.com/en-us/library/bxwfs976.aspx)). (2认同)

Red*_*ave 7

手动实现有限的 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;};
};
Run Code Online (Sandbox Code Playgroud)

这是我的第一个观察;离理想还很远,缺少很多细节。人们可以使用 的一个实例meta作为其假定的 实现的非静态数据成员std::any

  • @underscore_d 感谢您的关注。最初错过了这个片段,因为我不愿意添加。当时是-3,应该停在那里。但这就是社会雪崩效应,人们只是互相追随。我已经在评论中指出了更好的答案:已接受的答案。并且并不真正关心选票。请阅读霍尔特的。 (4认同)
  • 问题是:“这是如何实现的,或者这可能只是 gcc 的一个功能?” 它可以完成并不是一个答案,随着提供的代码编译和工作,它可以完成是非常清楚的! (2认同)
  • 好奇为什么这个值为 -5,而另一个答案以看似更模糊和不太线程安全的术语说同样的事情却为 +3...? (2认同)
  • 我投了赞成票。这是一个很好的技术背景。这个答案由于没有充分的理由而被鲁莽地否决了。 (2认同)