why doesn't std::any_cast support implicit conversion?

后端 未结 5 945
傲寒
傲寒 2020-12-24 12:33

Why does std::any_cast throw a std::bad_any_cast exception when an implicit conversion from the actual stored type to the requested type would be p

5条回答
  •  一向
    一向 (楼主)
    2020-12-24 13:21

    std::any has to be implemented with type-erasure. That's because it can store any type and can't be a template. There is just no other functionality in C++ to achieve this at the moment.

    What that means is that std::any will store a type-erased pointer, void* and std::any_cast will convert that pointer to the specified type and that's it. It just does a sanity check using typeid before to check whether you the type you cast it to is the one stored into the any.

    Allowing implicit conversions would be impossible using the current implementation. Think about it (ignore the typeid check for now).

    std::any_cast(a);
    

    a stores an int and not a long. How should std::any know that? It can just cast its void* to the type specified, dereference it and return it. Casting a pointer from one type to another is a strict aliasing violation and results in UB, so that's a bad idea.

    std::any would have to store the actual type of the object stored in it, which is not possible. You can't store types in C++ right now. It could maintain a list of types along with their respective typeids and switch over them to get the current type and perform the implicit conversion. But there is no way to do that for every single type that you are going to use. User defined types wouldn't work anyways, and you'd have to rely on things such as macros to "register" your type and generate the appropriate switch case for it1.

    Maybe something like this:

    template
    T any_cast(const any &Any) {
      const auto Typeid = Any.typeid();
      if (Typeid == typeid(int))
        return *static_cast(Any.ptr());
      else if (Typeid == typeid(long))
        return *static_cast(Any.ptr());
      // and so on. Add your macro magic here.
    
      // What should happen if a type is not registered?
    }
    

    Is this a good solution? No, by far. The switch is costly, and C++'s mantra is "you don't pay for what you don't use" so no, there is no way of achieving this currently. The approach is also "hacky" and very brittle (what happens if you forget to register a type). In short, the possible benefits of doing something like this is not worth the trouble in the first place.

    is there a workaround to allow an implicit conversion (in case the exact type that std::any holds is unknown)?

    Yes, implement std::any (or a comparable type) and std::any_cast yourself using the macro register approach mentioned above1. I won't recommend it though. If you don't and can't know what type std::any stores and need to access it, you have a possible design flaw.


    1: Don't actually know if this is possible, I'm not that good in macro (ab)use. You can also hard-code your types in for your custom implementation or use a separate tool for it.

提交回复
热议问题