创建一个函数,该函数返回具有现有类继承的不同类型

Create a function that returns different types with existing class inheritance

本文关键字:函数 继承 同类型 一个 创建 返回      更新时间:2023-10-16

所以有以下现有的类:

class Data {};
class String_data : public Data {
string m_data;
public:
string str() { return m_data; }
};
class Integer_data : public Data {
int m_data;
public:
int value() { return m_data; }
};

我正在尝试做一些类似重载函数的事情,但对于基类:

string get_data(String_data *data) {
return data->str();
}
int get_data(Integer_data *data) {
return data->value();
}

问题是这些对象作为基类的对象指针存储在容器中,如下所示:

std::map<string, Data*> data_list;
data_list.emplace( std::pair<string, Data*>("first", new String_data()) );
data_list.emplace( std::pair<string, Data*>("second", new Integer_data()) );

所以,我试图让它像这样工作:

string first_data = get_value(data_list["first"]);
int second_data = get_value(data_list["second"]);

编辑:对不起,这必须很长。我想让问题保持简单,但除非我陈述全部意图,否则它不会完全起作用。

data_list中的一些项目非常恒定,我想创建一种简单的方法来扩展项目列表,而无需创建包装类并为每个类提供单独的访问方法,如下所示:

class DataListWrapper {
map<string, Data*> m_data_list;
public:
string get_house() { return ((String_data*)m_data_list["house"])->str(); }
int get_loan() { return ((Integer_data*)m_data_list["loan"])->value(); }
// etc...
};

为了确保这些,开发人员不会意外地错误键入字符串键,我为编译器创建了全局常量以检查编译时。

#ifndef STRING_CONSTANTS
#define STRING_CONSTANTS
constexpr char c_house[] = "house";
constexpr char c_loan[] = "loan";
#endif

因此,目前我们这样做:

string house = ((String_data*)m_data_list["house"])->str();
int loan = ((Integer_data*)m_data_list["loan"])->value();
call_me(house, loan);

但是我想扩展列表,并通过执行非常简单和轻松的事情来轻松获得值,例如:

string house = get_value(m_data_list[c_house]);
int loan = get_value(m_data_list[c_loan]);
call_me(house, loan)

编辑:从某种意义上说,我想在没有类型转换的情况下做到这一点,因为它变得非常冗长,并且使人们远离代码试图对所有类型转换所做的事情。

您可以使用std::variant轻松实现这一点,它保留了告诉使用哪种类型的元数据。因此,您可以删除空基类:

using Data = std::variant<String_data, Integer_data>;
std::map<string, Data> data_list;
data_list.emplace("first", String_data{});
data_list.emplace("second", Integer_data{});

然后使用允许您检查基础数据的访问者:

auto get_data(String_data const& data) -> std::string {
return data.str();
}
auto get_data(Integer_data const& data) -> int {
return data.value();
}
std::visit(
[](auto& data) { auto value = get_data(data); },
data_list["first"]
);
std::visit(
[](auto& data) { auto value = get_data(data); },
data_list["second"]
);

但是,如果要保留类层次结构,则始终可以使用虚拟多态性实现自己的访问者:

struct Visitor {
virtual void operator()(int const&) const = 0;
virtual void operator()(std::string const&) const = 0;
};
struct PrintVisitor : Visitor {
void operator()(int const& value) const override {
std::cout << value;
}
void operator()(std::string const& value) const override {
std::cout << value;
}
};
struct Data {
virtual void visit(Visitor const&) const = 0;
};
struct String_data : Data {
void visit(Visitor const& v) const override {
v(m_data);
}
private:
std::string m_data;
};
struct Integer_data : Data {
void visit(Visitor const& v) const override {
v(m_data);
}
private:
int m_data;
};

然后使用它:

data_list["first"]->visit(PrintVisitor{});
data_list["second"]->visit(PrintVisitor{});

当然,如果您已经知道地图中元素的类型,则可以使用static_cast.但它需要您有足够的上下文来提前知道将有哪种类型:

string first_data = get_value(static_cast<String_data*>((data_list["first"]));
int second_data = get_value(static_cast<Integer_data*>((data_list["second"]));
call_me(first_data, second_data);

我建议不要使用dynamic_cast.当您要使用动态转换时,最好使用变体。以这种方式向下抛弃它意味着你知道你将要处理的类型数量,并且会像变体一样约束层次结构,但以一种更微妙和容易出错的方式。


由于您在问题中添加了更多详细信息,因此问题更清晰。

我的解决方案是不直接使用字符串,而是使用某种标识符来封装如何强制转换以及如何获取值。

struct house_data_t {
static constexpr auto index = "house";
using type = String_data; 
} inline constexpr house_data{};
struct loan_data_t {
static constexpr auto index = "loan";
using type = Integer_data; 
} inline constexpr loan_data{};

然后创建一个使用此元数据的函数:

std::string house = get_value(house_data, data_list);
int loan = get_value(loan_data, data_list);

get_value函数可以这样实现:

auto get_data(String_data* data) -> std::string {
return data->str();
}
auto get_data(Integer_data* data) -> int {
return data->value();
}
using data_list_t = std::map<std::string_view, std::unique_ptr<Data>>;

template<typename data_t, typename data_type_t = typename data_t::type>
auto get_value(data_t data, data_list_t& data_list) -> decltype(get_data(std::declval<data_type_t*>())) {
return get_data(static_cast<data_type_t*>(data_list[data.index].get()));
}

现场示例