Home > Software design >  Is there a way of creating an std::optional<> out of a nullable argument?
Is there a way of creating an std::optional<> out of a nullable argument?

Time:11-23

Languages such as Scala and Java have had for quite some time some sort of optional type. In both, there seems to be a constructor method allowing one to either pass in an object O or a null value. The first constructs an optional value with a copy of O inside it, the second, an empty option.

From what I can gather, such a thing is not possible with std::optional<>(v) and std::make_optional<>(v), as both throw a runtime exception when called with a nullptr argument.

Is there any other constructor function allowing me to do this in the standard library?

Thanks

CodePudding user response:

Since you didn't showed any code example, I will assume the following:

  • You have a function called f
  • The function f takes a potentially null int* as parameter (the nullable argument)
  • The function f returns a std::optional<int>
  • I will assume you have a similar function body in your codebase

Edit your question to add a more relevant examples if my assumptions are wrong.


So to recap the code example:

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

The problem don't lie in std::optional and its contructors, but in the fact that you dereference a null pointer. This happen before any constructor call. At this point, you're already in UB land, you cannot infer behaviour after this happens.

The fix is to check the pointer before constructing the optional value:

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

Now, the int you send to the std::optional constructor is always valid assuming ptr is valid or null, and you return an empty optional if not.

CodePudding user response:

An important property of std::optional is to not involve dynamic allocations. The object is (optionally) stored directly in the std::optional.

A std::optional<T> cannot hold a nullptr when T cannot have a nullptr value.

A std::optional can be constructed empty by calling the appropriate constructor:

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

CodePudding user response:

Maybe you want std::nullopt, it signifies that optional doesn't hold value, in other words optional nullopt is exactly what null means in other lanugages. In other words std::optional std::nullopt inroduce natural sence of null into C , same as in other languages.

Through std::nullopt you can control optional arguments in the function, like I did below for add() function.

You just check if (optional_value) to find out if it holds value, which is same as if (optional_value.has_value()). See doc here.

Also you can use .value_or(default) to return value or default, it is same as saying var or default or var if var is not None else default in Python for None-able argument.

You can also get value of optional just by *optional_value dereference, same like pointer. Doc here.

See all 4 possibilities of using nullable optional in implementation of add() function, it has 4 ways of doing same thing, choose which is better for you.

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;
}

Output:

8
8
10

In other words if you want any variable to hold both value of some type and also null, then just wrap it into std::optional, and use std::nullopt to signify null, like in following example:

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

See all docs about std::optional.

  • Related