提问人:bmetz 提问时间:11/9/2023 最后编辑:bmetz 更新时间:11/10/2023 访问量:84
从抽象类派生的类中获取从抽象类派生的类的可选项
Getting an optional of a class derived from abstract class, from a class derived from abstract class
问:
我有类似于下面的代码的东西,其中一组具有相似共享行为的类(Tool1、Tool2)都继承自抽象类(ITool)。所有这些类都拥有自己的相应类(Tool1Attachment、Tool2Attachment)的可选类,该类也继承自抽象类 (IAttachment)。
class ITool {
private:
virtual ? DoGetAttachment() = 0;
// Other shared behaviours...
public:
? GetAttachment() {return DoGetAttachment();}
// Other shared behaviours...
};
class Tool1: public ITool {
std::optional<Tool1Attachment> opt_attachment;
? DoGetAttachment() override;
public:
[...]
};
class Tool2: public ITool {
std::optional<Tool2Attachment> opt_attachment;
? DoGetAttachment() override;
public:
[...]
};
// --------------------------------
class IAttachment {
[...]
};
class Tool1Attachment : public IAttachment {
[...]
};
class Tool2Attachment : public IAttachment {
[...]
};
工具类具有可选是有意义的 - 在上下文中,它可能在任何给定时间具有也可能没有实际实例。
如果我有一个 IAttachment,并且想获得 IAttachment,就会出现一个问题。
我最初为此使用了指针,即 ,它“工作正常”。但是,它似乎丢失了 optional 提供的一些对缺失值的更明确的检查,从而使将来更容易遇到 nullptr 问题。IAttachment* GetAttachment();
我还尝试了带有可选的不同结构(std::reference_wrapper),但一直遇到无效的协变错误。
有没有一种方法可以允许返回可选(或类似的结构)?返回类型应该是什么样子的?或者带有 nullptr 检查的指针在这里最合适吗?
? GetAttachment() {return DoGetAttachment();}
答:
指针是显而易见的解决方案(如果可选为空,则指针为空)。如果使用继承,则无论如何都需要指针(或引用)才能使多态性起作用,并且 C++ 支持协变返回类型,以便可以返回 .在返回的指针上检查 null 几乎是标准做法。另一种选择是返回一个(因为协方差在这里不起作用),但除了额外的复杂性之外,我看不出这会给你带来什么。Owner1
Owned1*
optional<IOwned*>
评论
std::optional<std::reference_wrapper<IOwned>>
将是一种可能性,但 IMO 它没有添加任何内容来更喜欢它而不是指针
and_then
value_or
transform
std::optional
std::optional
std::optional
std::optional
std::optional
答案实际上归结为您对该功能的期望。它是否应该将成员的可选性“转发”给调用者,并负责检查值是否存在?或者,如果存储的函数有值,则函数是否应该返回引用,如果没有,则抛出异常?DoGetAttachment
DoGetAttachment
std::optional
假设您要将可选性“转发”给函数的调用者,您基本上有两个选项。
选项 1:使用(原始)指针
使用指针,就像您已经根据自己的问题所做的那样。这是最简单的解决方案和标准做法。
选项 2:与std::optional
std::reference_wrapper
std::optional
不是多态的,因此不能将 a 转换为 .您可以做的是返回 an 或 .恕我直言,我更喜欢 ,因为否则您在两个级别上具有“可选性”属性:一次用于存储的指针,一次用于存储的指针。因此,您需要检查两次是否存在。std::optional<Derived>
std::optional<Base>
std::optional<Base*>
std::optional<std::reference_wrapper<Base>>
std::reference_wrapper
std::optional
选项 2 会给您的代码带来一些额外的复杂性,由您决定是否值得。
使用的参数是,那std::optional
- 返回值的“可选性”是通过语义传达的。返回的指针不会自动传达该信息;
- 返回 an 允许您使用 的 一元运算符,例如 、 、(其中一些是 C++23 功能);并且
std::optional
std::optional
value_or
and_then
transform
- 与拥有(原始)指针相比,S 不执行任何动态内存分配(如果需要的话)。
std::optional
这是一个实现,其中返回 .DoGetAttachment
std::optional<std::reference_wrapper<IToolAttachment>>
#include <optional>
#include <iostream>
struct IAttachment {
virtual ~IAttachment(){}
virtual std::string foo() const = 0;
};
struct Tool1Attachment : IAttachment
{
std::string foo() const override { return "Tool1Attachment"; };
};
struct Tool2Attachment : IAttachment
{
std::string foo() const override { return "Tool2Attachment"; };
};
struct ITool {
virtual ~ITool(){}
using IAttachmentCRef = std::reference_wrapper<IAttachment const>;
virtual std::optional<IAttachmentCRef> DoGetAttachment() const = 0;
};
template <typename T>
struct Tool: public ITool {
std::optional<T> opt_attachment;
std::optional<IAttachmentCRef> DoGetAttachment() const override
{
return opt_attachment.transform([](auto& v){ return std::ref(v); });
}
};
using Tool1 = Tool<Tool1Attachment>;
using Tool2 = Tool<Tool2Attachment>;
int main()
{
auto print = [](ITool const& tool) {
return tool.DoGetAttachment()
.transform([](auto v){ return v.get().foo(); })
.value_or("No value.");
};
Tool1 tool1;
Tool2 tool2;
tool2.opt_attachment = Tool2Attachment();
std::cout << print(tool1) << "\n";
std::cout << print(tool2) << "\n";
}
输出:
No value.
Tool2Attachment
评论
DoGetOwned
DoGetOwned
const
nullptr
std::nullopt
std::optional