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 nullint*
as parameter (the nullable argument) - The function
f
returns astd::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.
#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.