C++ 17 – Optional Template Class
You have a function that takes a vector of elements as an argument. The function needs to find the first negative number in the vector. There are two return values possible. If it is found, the negative number needs to be returned. When not found, it should return NULL.
Is there a way to have a return value for the function that can hold both these values? Further, is there a way for the client code that uses this function to check if the return value was meaningful or not?
The most standard way for this is to use the std::optional template class introduced in C++ 17.
The function prototype would look as below. The function can return an integer or std::nullopt.
// Other headers
#include <optional>
std::optional<int> find_first_negative_number( // Pass vector as argument here ) {
// code to find negative number
if found return the negative number
else return return std::nullopt;
}
Client code can check the result as below.
auto result = find_first_negative_number( // Pass the vector );
if(result) { // Print the number }
else { // Print that no negative numbers found }
The complete program using this concept is as below. Also, see in the comments about different functionalities provided that be used on the std::optional value for post-processing on the result. The program is available for download at https://github.com/codeversionmaster/cplusplus/blob/cplusplus17/std_optional.cpp.
$ cat std_optional.cpp
#include <iostream>
#include <optional>
#include <vector>
using namespace std;
optional<int> first_negative(const vector<int>& numbers) {
for (const auto& num : numbers) {
if (num < 0) {
return num;
}
}
// If no negative number found, return nullopt
return nullopt;
}
int main() {
vector<int> numbers{-1, 2, 3, -4, 5};
auto result = first_negative(numbers);
// 1. Use operator* and operator-> to print value in the optional.
cout << "Using operator*: " << *result << endl;
// 2. Use operator bool() to check if result has meaningfule value
if (result) {
cout << "Optional contains a value." << endl;
} else {
cout << "Optional is empty." << endl;
}
// 3. has_value(): Check if the optional contains a value.
if (result.has_value()) {
cout << "Optional has a value using has_value(): " << *result << endl;
} else {
cout << "Optional is empty using has_value()." << endl;
}
// 4. value(): Access the value stored in the optional or throw an exception.
try {
cout << "Value using value(): " << result.value() << endl;
} catch (const bad_optional_access& e) {
cout << "Exception caught: " << e.what() << endl;
}
// 5. value_or(T): Access the value stored in the optional, or return the provided default value.
cout << "Value using value_or(): " << result.value_or(0) << endl;
return 0;
}
You can compile and run the program as below.
$ g++ -std=c++17 std_optional.cpp -o std_optional
$ ./std_optional
Using operator*: -1
Optional contains a value.
Optional has a value using has_value(): -1
Value using value(): -1
Value using value_or(): -1
You can also try compiling with C++ 14 and see that this feature is not supported before C++ 17.
$ g++ -std=c++14 std_optional.cpp -o std_optional
std_optional.cpp:6:1: error: ‘optional’ does not name a type
6 | optional<int> first_negative(const vector<int>& numbers) {
| ^~~~~~~~
...
...