【问题标题】:Is there a way of creating an std::optional<> out of a nullable argument?有没有办法从可为空的参数中创建一个 std::optional<> ?
【发布时间】:2022-01-01 02:37:14
【问题描述】:

Scala 和 Java 等语言在相当长的一段时间内都有某种可选类型。在这两种方法中,似乎都有一个构造方法允许传入对象O 或空值。第一个构造一个可选值,其中包含O 的副本,第二个是一个空选项。

据我所知,std::optional&lt;&gt;(v)std::make_optional&lt;&gt;(v) 不可能发生这样的事情,因为当使用 nullptr 参数调用时,两者都会引发运行时异常。

在标准库中是否有任何其他构造函数允许我这样做?

谢谢

【问题讨论】:

  • std::optional 存储对象(可选)而不是指针。
  • 听起来你在找std::nullopt
  • 什么是“可能为空的参数”? std::optional 的“null”不是 nullptr 而是 nullopt
  • 你能在你的问题中添加一些想要的行为吗?这应该有助于我们更好地理解您想要什么,因为 C++ 并没有真正可以为空的对象。 C++ 中的所有对象都有一个值。即使是空指针也有0 的值,这被视为特殊的指针值。
  • 并非 C++ 中的每个对象都是“可空的”。根据您要求的行为,我认为每个std::optional&lt;T&gt; 都可以为空,并且该空值为std::nullopt。如果您希望 C++ 中的所有变量都是“可空的”,则对所有变量使用 std::optional&lt;T&gt;

标签: c++ stdoptional


【解决方案1】:

std::optional 的一个重要属性是不涉及动态分配。该对象(可选)直接存储在std::optional 中。

T 不能有nullptr 值时,std::optional&lt;T&gt; 不能保存nullptr

通过调用适当的constructor,可以将std::optional 构造为空:

// either
std::optional<int> x;                // does not contain an int
// or 
std::optional<int> y{std::nullopt};  // does not contain an int

【讨论】:

  • 嗨。那不是我问的。请看stackoverflow.com/a/4692607/130758
  • @devouredelysium 同时我明白这不是你问的,但我不明白你问的是什么。请澄清问题。在 Java 中,任何东西都可以是 null,但在 C++ 中则不行,因此您的类比不足以知道您在寻找什么
  • @devouredelysium 我不懂那种语言。你能创建一个小的 C++ 示例来完成它应该做的事情吗?或者至少是触发您遇到的错误的示例?
【解决方案2】:

由于您没有显示任何代码示例,我将假设以下内容:

  • 您有一个名为f 的函数
  • 函数f 将可能为空的int* 作为参数(可空参数)
  • 函数f返回一个std::optional&lt;int&gt;
  • 我假设您的代码库中有类似的函数体

如果我的假设有误,请编辑您的问题以添加更相关的示例。


回顾一下代码示例:

auto f(int* ptr) -> std::optional<int> {
    return std::optional<int>{*ptr};
}

问题不在于std::optional 及其构造函数,而在于您取消引用空指针这一事实。这发生在任何构造函数调用之前。此时,您已经处于 UB 领域,发生这种情况后您无法推断行为。

修复方法是在构造可选值之前检查指针:

auto f(int* ptr) -> std::optional<int> {
    return ptr ? std::optional<int>{*ptr} : std::nullopt;
}

现在,假设 ptr 有效或为 null,您发送给 std::optional 构造函数的 int 始终有效,如果不是,则返回一个空的可选项。

【讨论】:

    【解决方案3】:

    也许你想要std::nullopt,它表示 optional 不具有价值,换句话说 optional + nullopt 正是 null 在其他语言中的含义。换句话说,std::optional + std::nullopt 将 null 的自然意义引入 C++,与其他语言相同。

    通过std::nullopt,您可以控制函数中的可选参数,就像我在下面对add() 函数所做的那样。

    您只需检查if (optional_value) 以了解它是否具有价值,这与if (optional_value.has_value()) 相同。见doc here

    您也可以使用.value_or(default) 来返回值或默认值,这与Python 中的var or defaultvar if var is not None else default 用于None-able 参数相同。

    你也可以通过*optional_value取消引用来获取可选的值,就像指针一样。 Doc here.

    查看在实现add() 函数时使用可空选项的所有4 种可能性,它有4 种做同样事情的方式,选择更适合你的。

    Try it online!

    #include <optional>
    #include <iostream>
    
    int add(int x, std::optional<int> y = std::nullopt) {
        return x + y.value_or(5);
        // also possible to do same like this
        return x + (y ? y.value() : 5);
        // or same as
        return x + (y ? *y : 5);
        // or same as
        if (y)
            return x + *y;
        else
            return x + 5;
    }
    
    int main() {
        std::cout << add(3) << std::endl;
        std::cout << add(3, std::nullopt) << std::endl;
        std::cout << add(3, 7) << std::endl;
    }
    

    输出:

    8
    8
    10
    

    换句话说,如果您希望任何变量同时保存某种类型的值和 null,那么只需将其包装到 std::optional 中,并使用 std::nullopt 来表示 null,如下例所示:

    SomeClass obj; // non-nullable, can't be null
    std::optional<SomeClass> obj2; // almost same as above but now is nullable
    obj2 = obj; // you can naturally assign value of object
    obj2 = std::nullopt; // this way you set variable to null
    obj = *obj2; // this way you get value of object by using * dereference
    obj = obj2.value(); // same as above instead of *
    obj = obj2 ? *obj2 : default_value; // this way you check if obj2 is null, if not then get it's value through *, otherwise return default
    obj = obj2.value_or(default_value); // same as last line above
    if (obj2) DoSomething(); // checks if object is not null
    if (obj2.has_value()) DoSomething(); // same as above
    

    有关 std::optional 的信息,请参阅 all docs

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多