对对象的可选引用:最佳方式?

optional refence to an object: best way?

提问人:mu5e 提问时间:6/20/2023 最后编辑:mu5e 更新时间:6/20/2023 访问量:184

问:

我希望函数返回对对象的可选引用。想法是避免复制,但似乎我不应该根据这个讨论使用: std::可选的引用类型专用化

假设我有一个对象的映射,我希望我的函数返回对对象的引用(如果映射中存在该对象)。最好的方法是什么?

struct HeavyObj;
std::map<int, HeavyObj> my_map;

std::optional<HeavyObj&> get_obj(int key){   
    if(auto it = my_map.find(key); it != my_map.end()){
        return std::optional<HeavyObj&>{ ? };
    } else {
        return {};
    }

ps:C++17 需要

更新,如果我使用指针,我得到:



#include <optional>
#include <map>
struct HeavyObj{
    int data;
};
static std::map<int, HeavyObj> my_map;

std::optional<HeavyObj*> get_obj(int key){   
    if(auto it = my_map.find(key); it != my_map.end()){
        return std::optional<HeavyObj*>{&(*it)};
    } else {
        return {};
    }
}

int main(){
    auto test  = get_obj(3);
    return 0;
}

// gcc -std=c++17 -Wall optional.cc -lstdc++
optional.cc: In function ‘std::optional<HeavyObj*> get_obj(int)’:
optional.cc:13:47: error: no matching function for call to ‘std::optional<HeavyObj*>::optional(<brace-enclosed initializer list>)’
   13 |         return std::optional<HeavyObj*>{&(*it)};
      |                                               ^
    ```
C++ 参考 stdoptional

评论

2赞 user17732522 6/20/2023
HeavyObj*std::optional<std::reference_wrapper<HeavyObj>>
0赞 Paul Sanders 6/20/2023
请将其添加到您的问题中 - 它太密集了,无法在评论中阅读。返回 a 将是我个人的选择。HeavyObj *
0赞 Retired Ninja 6/20/2023
如果要返回指针,则无需将其包装在可选项中。 意味着它没有被发现,其他任何东西都意味着它被找到了。nullptr
0赞 fana 6/20/2023
对于指针代码,对应的不是 .HeavyObjit->second*it
1赞 n. m. could be an AI 6/20/2023
“可选引用”称为“指针”。

答:

0赞 UnquoteQuote 6/20/2023 #1

如前所述,您可以使用未初始化的指针这一事实来模拟该行为。nullptroptional

但是,有些人可能会认为它无论如何都使用起来更干净,因为如果您有一个类似的函数返回一个,例如,它会更加一致。optionaloptionalint

当找不到该值时,您可以使用 ,如果找到该值,则直接返回该值(编译器将执行从 HeavyObject* 到 optional<HeavyObject* 的强制转换>但您可以手动执行)。std::nullopt

评论

0赞 Evg 6/20/2023
std::optional<T*在这种情况下,>是错误接口的一个例子。从逻辑上讲,有两种类型的返回值 - 指向找到对象的指针和对应于丢失对象的某个值。 保存三种状态(空可选、空指针和非空指针),而不是两种。所以它不是一个更干净的退货类型,它只是一个错误的退货类型。std::optional<T*>
0赞 UnquoteQuote 6/20/2023
我同意你的看法,不会那样使用它。但纯粹主义者可能会说,指针应该始终初始化。无论如何,我认为展示如何使用可选很重要,但我认为你的观点也很重要,因为在实践中它只是浪费资源。
3赞 Retired Ninja 6/20/2023
“未初始化的指针是” 不,它不是,它是一个未初始化的指针,其值不确定。 可以指向任何地方。 初始化为 。nullptrint *p;int *p = nullptr;nullptr
1赞 Evg 6/20/2023
纯粹主义者可能会说,指针应该始终被初始化 - 是指针最纯粹的初始化。;)指针本身的想法可以为空。nullptr
0赞 Jan Schultke 6/20/2023 #2

返回 a ,它是惯用的解决方案。C++ 标准库返回指针,这些指针在许多地方充当可选引用。例如:HeavyObj*

  • std::get_if(std::variant)
  • std::any_cast<T*>(std::any*)

这些都是C++17函数,所以它们绝不是90年代的过时设计。 在您的情况下,我们可以按如下方式使用原始指针:

HeavyObj* get_obj(int key){   
    if(auto it = my_map.find(key); it != my_map.end()){
        return std::addressof(*it);
    } else {
        return nullptr;
    }
}

注意:我们使用 std::addressof,以防 HeavyObj 有一个过载的 address-of 运算符。

问题std::optional

std::optional<HeavyObj*>会有点问题,因为它在两个方面是可选的:

  • 可能没有价值,或者std::optional
  • 里面可能是一个HeavyObj*nullptr

您只需要一层可选性,因此请使用 .HeavyObj*

如果您迫切希望避免使用原始指针,那么解决方案将是:

std::optional<std::reference_wrapper<HeavyObj>> get_obj(int key){   
    if(auto it = my_map.find(key); it != my_map.end()){
        return std::ref(*it);
    } else {
        return {};
    }
}

但是,这并不是很符合人体工程学,因为返回类型非常长。