如何在不进行强制转换的情况下调用变量上的函数



我有三个类,而第一个类的属性可以是任何其他类的类型:

#include<variant>   
class A {
public:
std::string to_string(){
return var.to_string();
}
std::variant<B,C> var;
};
class D {
virtual std::string to_string();
};
class B : public D {
public:
std::string to_string();
};
class C : public D {
public:
std::string to_string();
};

我想在类a的成员上调用一个方法(比如to_string(。我知道每个变体都有函数的实现。

我想出的最有效的方法是来自cppreference:

if(const B* b = std::get_if<B>(&var))
b->to_string(); 
if(const C* c = std::get_if<C>(&var))
c->to_string();

尤其是在处理许多变体时,这似乎是无效和丑陋的,我认为这应该可以通过一句话实现。有更好的方法吗?

正如注释中所指出的,由于CB继承自D,因此您只需存储一个std::unique_ptr<D>(它必须是一个指针才能通过对象切片获得动态调度(。但是,如果你的现实世界中的程序有一些限制,使其无法实现,那么当涉及到std::variant时,你最好的朋友就是std::visit

std::visit接受两个参数:一个访问者和一个变体。访问者必须是某种形式的对象,对于每个变体选项(在您的情况下是BC(,该对象是可调用的(即定义了operator()(。然后,它为std::variant中的实际调用适当的operator()

我们可以创建一个新的类,为我们定义所有这些可调用项。这会很乏味。

class MyCallable {
std::string operator()(B& arg) {
return arg.to_string();
}
std::string operator()(C& arg) {
return arg.to_string();
}
};
...
std::visit(MyCallable(), var)

(注意:如果您将D上的to_string设置为const方法,那么您可以使用const T&而不是左值引用,这可能在这里更好(

但这很愚蠢,而且非常冗长。我们也可以将其模板化。这样可以减少一些样板工作。

class MyCallable {
template <typename T>
std::string operator()(T& arg) {
return arg.to_string();
}
};
...
std::visit(MyCallable(), var)

这稍微好一点,但我们必须为此定义一个完整的类,这很愚蠢。幸运的是,现代C++将允许您定义模板化了operator()的lambda,就像这样,但却是内联的。所以你可以简单地做这个

std::visit([](auto& x) { return x.to_string(); }, var)

这将适用于任何std::variant,前提是所有变体选项都有兼容的to_string方法(它们甚至不必来自同一个超类;它们可以是不相关的to_string方法(。

相关内容

  • 没有找到相关文章

最新更新