调用存储在std::any中的对象的成员函数

问题描述 投票:0回答:1

假设我有一个模板类,如下所示:

template<class T, class V>
class MyClass
{
    public:
    MyClass(std::string const& name){
        s = name;
    }

    T return_var1() {
        return var1;
    }

    std::string s;
    T var1;
    V var2;
};

我将如图所示使用它:

class AnotherClass {
    public:

    void some_func() {
        MyClass<int, double> my_var("test");
        std::pair<std::string, std::any> tmp_pair("test", my_var);
        my_vars.insert(tmp_pair);
    }

    void some_other_func() {
        auto tmp = my_vars["test"];
        
        // Any way to call member function without using std::any_cast<...> ?
        // For example, not like this:
        // (std::any_cast<MyClass<int, double>>tmp).return_var1()
        // But like this:
        std::cout << tmp.return_var1();
    }

    private:
    std::unordered_map<std::string, std::any> my_vars;
}

有没有办法在不知道或不使用所包含对象的类型的情况下调用存储在 std::any 中的对象的成员函数? (不使用上面评论中所写的 std::any_cast<>()

任何解决方案都是可以接受的,例如使用 CRTP 从基类继承 MyClass,使用

std::variant
等等。

我需要这个的原因是因为我想使用这样的模板化函数:

some_templated_function<decltype(my_var.var1)>();

// Or similarly,
some_templated_function<decltype(my_var.return_var1())>();

目的是允许用户仅注册类型

<int, double>
一次,并在需要时广泛使用类型名称。

c++ templates polymorphism rtti crtp
1个回答
0
投票

是的,只要应该调用的函数具有已知的签名,这是可能的。我将使用一个更简单的例子来描述事情,就像你的OP中的例子一样。假设您有两个类提供具有固定签名的成员函数

greet

struct A
{
    void greet() const { std::cout<< "hello" << std::endl; }
};

struct B
{
    void greet() const { std::cout<< "goodbye" << std::endl; }
};

使用

std::any
,您可以包装这些类的任何实例:

std::any a = A();
a = B();

但是,为了使用包含的对象,您始终必须知道当前包含的确切类型。如果您有许多类型或者这些类型不是您自己定义的(例如在库中),则这是不幸的。以下代码给出了克服此要求的基本方法:

struct any_caller
{
    template<typename T>
    any_caller(T object)
        : any_(object)
        , greet_([](std::any const& a){ return std::any_cast<T const&>(a).greet();})
    {}

    std::any any_;
    void (*greet_)(std::any const&);
    auto greet() const { return greet_(any_); }
};

想法如下:

  • 使用要调用的所需函数的确切签名定义函数指针。这将指向一个 lambda,它执行任意对象中函数的实际调用。
  • 在构造时,并且只有在那里,您才知道传递给
    std::any
    的对象的类型。您可以使用此信息来设置执行
    std::any_cast
    的 lambda,然后调用所需的函数。
  • 这是类型擦除的一种形式,因为您正在从存储的实际对象类型中进行抽象,并且只有在您知道要调用的确切签名时才有效。

上面的代码可以按如下方式使用:

int main()
{
    any_caller c = A();
    c.greet();  //prints "hello"

    c = B();
    c.greet();  //prints "goodbye"
}

Godbolt 上的演示

这个基本示例可以进一步扩展以支持多个函数,从而产生一种称为“无指针继承”的技术,如此处所述。

© www.soinside.com 2019 - 2024. All rights reserved.