微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

基类指针列表,获取函数成员的不同返回值

如何解决基类指针列表,获取函数成员的不同返回值

想象以下情况

  • 您有一个指向变量的指针:fooVar *
  • 您可以使用如下函数获取变量的类型:get_type(fooVar* foo) 返回一个 int 值 bar
  • 然后您可以在 enum 中查找返回值 bar 所属的类型,例如1 -> type_int
  • 根据类型,您可以调用以下函数之一来获取值:double get_double(fooVar*)int get_int(fooVar*)、...

目标是

将这些函数(C 库的简化版本)包装到 CPP 类中。

问题是

  • 我想存储所有变量的列表。
  • 如何统一返回变量的当前值?

我尝试创建一个 BaseType 并从中派生其他类型,如下所示:

BaseClass
|--> IntClass - int getValue()
|--> DoubleClass - double getValue()
'--> BoolClass - bool getValue()

然后我可以为每个派生类创建一个具有正确返回类型的成员函数 .getValue()(如上所示)。 由于我将指向所有变量的指针存储在类型为 BaseClass* 的列表中,因此我的编译器现在抱怨在我尝试调用它时未定义 getValue()

第二种解决方案可能是创建 double getAsDouble()bool getAsBool()int getAsInt(),然后在类型不可转换或不适合时抛出错误。但这也感觉不对。

第三种解决方案可能是不仅存储指针,还存储它指向的(派生类的)类型。稍后我可以投射 BasePointer。但这真的是个好主意吗?

另一种解决方案可能涉及 std::variant,但我认为这太过分了 - 对吧?

问题是:您将如何在 C++ 中做到这一点?有没有一种干净的方法来实现这一目标?


旁注:在这里读到的关于 SO 的所有主题都没有真正适合,对我来说这感觉像是一个标准问题,所以我想学习一些专业程序员解决此类问题的方法

解决方法

您似乎陷入了尝试重新实现虚函数和动态继承的困境。

因此,也许您能做的最直接的事情是拥有一个 virtual BaseType::getValue() 函数,该函数被特定于子类的 getValue() 覆盖。

但是现在,我们尽量避免使用原始指针数组 - 这需要显式分配和取消分配,并且容易出错(例如 - 如果抛出异常会发生什么?)。至少,让它像一个 std::vector<std::unique_ptr<BaseType>>(或 std::shared_ptr 而不是 std::unique_ptr)。如果您还没有听说过这些类似指针的类,请阅读:

What is a smart pointer and when should I use one?

除此之外 - 如果您事先知道可能的类型集,并且它不是很大 - 那么,正如您自己建议的那样 - std::variant 可能是一个合理的选择。 C++ 中的变体有点笨拙,但它们非常安全;一个你习惯了它们,它们足够方便。因此,就您而言:

using foo_var = std::variant<IntClass,DoubleClass,BoolClass>;

//...

auto foo_vars = get_array_of_foo_vars_somehow();

for(const foo_var& : foo_vars) {
    your_complex_visit([]auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T,int>)
            std::cout << "int with value " << arg << '\n';
        else if constexpr (std::is_same_v<T,double>)
            std::cout << "double with value " << arg << '\n';
        else if constexpr (std::is_same_v<T,bool>)
            std::cout << "bool with value " << arg << '\n';
        else 
            static_assert(always_false_v<T>,"non-exhaustive visitor!");
    },foo_var);
}

// Note: No need to `free()` anything when the array goes out of scope

现在,your_complex_visit() 有点像 std::visit(),只是您查看的是 arg.getValue() 而不是 args()。或者您可以在上面使用 std::visit,但需要将类型更改为外部类型(IntClass 而不是 'int' 等)

,

第二种解决方案可能是创建 double getAsDouble()bool getAsBool()int getAsInt(),然后在类型不可转换或不适合时抛出错误。但这也感觉不对。

发生错误时抛出异常的另一种方法是为 std::optional<double>std::optional<bool> 和 {{ 返回 std::optional<int>getAsDouble()getAsBool() 1}},分别。这样,返回一个空的 getAsInt() 将表示失败 - 即没有这样的值可以检索 - 这不是错误。

例如,如果您有以下对应于 C 代码的声明:

std::option

您可以编写一个包装类,其中包含用户定义的 struct FooVar; int get_type(const FooVar*); int get_int(const FooVar*); double get_double(const FooVar*); #define INT_TYPE 1 #define DOUBLE_TYPE 2 FooVar * 转换运算符:

const FooVar *

请注意,由于转换运算符,您仍然可以直接使用 C API 与此包装类,例如,您可以使用 class FooVarWrapper { FooVar *ptr_; public: // ... operator FooVar*() noexcept { return ptr_; } operator const FooVar*() const noexcept { return ptr_; } // ... }; 对象作为参数调用原始函数 get_type(),因为这将被隐式转换为存储的 FooVarWrapper

然后,您也可以将这些 getter 函数定义为非成员函数:

FooVar *

但是,您可能需要考虑将转换运算符标记为 std::optional<int> getAsInt(const FooVarWrapper& obj) { if (INT_TYPE != get_type(obj)) return std::nullopt; return get_int(obj); } std::optional<double> getAsDouble(const FooVarWrapper& obj) { if (DOUBLE_TYPE != get_type(obj)) return std::nullopt; return get_double(obj); } // ... similarly for getAsBool() ,以避免通过直接调用 explicitstd::optional 轻松绕过这些返回 get_int() 的函数。这样,每当您想将 get_double() 对象转换为存储的指针时,您都需要编写 static_cast<>;转换不会隐式发生。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。