C++17stladvanced
std::optional
C++17 · Practical Modern · Compiler flag: -std=c++17
Explanation
Represents a value that may or may not be present, providing a type-safe alternative to sentinel values, null pointers, or out-parameters. It makes function signatures self-documenting about the possibility of absence.
Real-World Scenario
Return an optional user record from a database lookup that may find no matching row.
Quick Snippet
snippet.cpp
#include <optional>
#include <string>
std::optional<std::string> findUser(int id) {
if (id == 1) return "Alice";
return std::nullopt;
}
if (auto user = findUser(1); user.has_value()) {
std::cout << "Found: " << *user << "\n";
}Full Example
optional.cpp
// CodeNebula - C++17 Example: std::optional
// Compile: g++ -std=c++17 -Wall -Wextra -o example optional.cpp
// Demonstrates std::optional, a type that may or may not hold a value.
// It replaces sentinel values (-1, nullptr, etc.) and makes the "no value"
// case explicit and type-safe.
#include <iostream>
#include <optional>
#include <string>
#include <vector>
#include <cmath>
// ------------------------------------------------------------------
// 1. Function that may fail: return optional instead of sentinel
// ------------------------------------------------------------------
std::optional<double> safe_sqrt(double x) {
if (x < 0.0) return std::nullopt;
return std::sqrt(x);
}
// ------------------------------------------------------------------
// 2. Lookup that may not find a result
// ------------------------------------------------------------------
std::optional<std::string> find_user(int id) {
std::vector<std::pair<int, std::string>> db = {
{1, "Alice"}, {2, "Bob"}, {3, "Carol"}
};
for (const auto& [uid, name] : db) {
if (uid == id) return name;
}
return std::nullopt;
}
// ------------------------------------------------------------------
// 3. Chaining optional results
// ------------------------------------------------------------------
std::optional<int> parse_int(const std::string& s) {
try {
std::size_t pos = 0;
int val = std::stoi(s, &pos);
if (pos == s.size()) return val; // entire string consumed
return std::nullopt;
} catch (...) {
return std::nullopt;
}
}
int main()
{
// 1. Basic construction and access
std::cout << "=== construction ===" << std::endl;
std::optional<int> a; // empty
std::optional<int> b = 42; // contains 42
std::optional<int> c = std::nullopt; // explicitly empty
std::optional<int> d{std::in_place, 99}; // in-place construction
std::cout << " a has_value: " << std::boolalpha << a.has_value() << std::endl;
std::cout << " b has_value: " << b.has_value() << ", value: " << *b << std::endl;
std::cout << " c has_value: " << c.has_value() << std::endl;
std::cout << " d has_value: " << d.has_value() << ", value: " << d.value() << std::endl;
// 2. value_or: provide a default
std::cout << "\n=== value_or ===" << std::endl;
std::cout << " a.value_or(-1): " << a.value_or(-1) << std::endl;
std::cout << " b.value_or(-1): " << b.value_or(-1) << std::endl;
// 3. safe_sqrt
std::cout << "\n=== safe_sqrt ===" << std::endl;
for (double x : {25.0, -4.0, 0.0, 2.0}) {
auto result = safe_sqrt(x);
std::cout << " sqrt(" << x << ") = ";
if (result) {
std::cout << *result << std::endl;
} else {
std::cout << "undefined (negative)" << std::endl;
}
}
// 4. Lookup
std::cout << "\n=== find_user ===" << std::endl;
for (int id : {1, 2, 5}) {
auto user = find_user(id);
std::cout << " id=" << id << ": "
<< user.value_or("(not found)") << std::endl;
}
// 5. Parsing with optional
std::cout << "\n=== parse_int ===" << std::endl;
for (const auto& s : {"42", "abc", "100", "12x"}) {
auto val = parse_int(s);
std::cout << " \"" << s << "\" -> "
<< (val ? std::to_string(*val) : "nullopt") << std::endl;
}
// 6. Resetting and emplacing
std::cout << "\n=== reset and emplace ===" << std::endl;
std::optional<std::string> opt = "hello";
std::cout << " before reset: " << *opt << std::endl;
opt.reset();
std::cout << " after reset: " << (opt ? *opt : "(empty)") << std::endl;
opt.emplace("world");
std::cout << " after emplace: " << *opt << std::endl;
return 0;
}
/*
Expected output:
=== construction ===
a has_value: false
b has_value: true, value: 42
c has_value: false
d has_value: true, value: 99
=== value_or ===
a.value_or(-1): -1
b.value_or(-1): 42
=== safe_sqrt ===
sqrt(25) = 5
sqrt(-4) = undefined (negative)
sqrt(0) = 0
sqrt(2) = 1.41421
=== find_user ===
id=1: Alice
id=2: Bob
id=5: (not found)
=== parse_int ===
"42" -> 42
"abc" -> nullopt
"100" -> 100
"12x" -> nullopt
=== reset and emplace ===
before reset: hello
after reset: (empty)
after emplace: world
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example optional.cpp