通过 std::any 向上转换

joe*_*ech 4 c++ std c++17 stdany

在 C++ 中,如果是 的基类,则可以将 的实例传递Derived给接受 的函数。这对于满足 API 非常有用,也是 API 的一种非常常见的设计模式。BaseBaseDerived

目前我面临着一种情况,我想通过std::any. 也就是说,我有std::any一个存储 的实例Derived,并且我想将其地址转换为BaseAPI 函数中的指针,该函数不应该知道 的存在Derived。我的用例是一个运行时反射库,它传递std::any存储反射类型的实例。

我知道向上转换std::any是不可能的,因为std::any_cast在转换之前检查typeid并返回 anullptr如果类型不匹配,请参阅cppreference

但也许有一个解决方法或一些我可以使用的聪明技巧?感觉这一定是可能的,因为向上转换在 C++ 中是很常见的事情,并且std::any 已经需要的地址和类型。

这是一个代码示例。以下代码按预期出现段错误,因为std::any_cast返回的 a在下一行中被取消引用nullptrany_function编译器浏览器:https://godbolt.org/z/E9sG9G3ff

#include <any>
#include <iostream>

class Base 
{
public:
    double get_val() const {
        return val;
    }

private: 
    double val {1.23};
};

void normal_function(Base const& b){
    std::cout << b.get_val() << std::endl;
};

void any_function(std::any const& b){
    auto* ptr = std::any_cast<Base>(&b);
    std::cout << ptr->get_val() << std::endl;
}

class Derived : public Base {};

int main()
{
    Derived d;
    
    // normal upcasting

    normal_function(d);

    // upcasting thru std::any

    std::any ad = d;
    any_function(ad);
    
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 7

any是类型安全的void*。这意味着它遵守 a 的规则void*。也就是说,如果将 a 强制转换T*为 a void*,则唯一可以将其强制转换回的类型T*是。不是到一个U*whereU的基类T。它必须是精确且唯一的T*

也同样如此any

any(就像 的使用void*)适用于代码 A 需要通过某个中介 B 与代码 C 进行通信的情况。A 和 C 就涉及的类型达成一致,但 B 不需要知道正在通信的类型(可能是因为B 促进许多不同的 As 和 C 对之间的交易。

就所涉及的类型达成一致意味着所涉及的确切类型。如果 A 正在与仅采用基类的代码进行通信,那么这就是它需要提供的内容(通过存储指向其派生类实例的基类的指针)。

现在,您所要求的总体想法并非不合理。然而,实施它是……困难的。大多数形式的类型擦除都可以处理具有类似行为的类型,但需要在擦除时而不是使用功能时了解被擦除类型的行为。如果一个类型需要转换为某种类型,那么当该对象添加到any. 这将成为假设类型的接口的一部分any,因此它将被烘焙到其代码中。

它不能采用“any”类型,然后询问“嘿,你可以转换为吗ThisType?”