Skip to content

Prefer std::optional over std::unique_ptr for optional values

When you need to represent optionality/nullability (“might have a value”), prefer std::optional over std::unique_ptr for simpler value semantics.

  • It’s a value: there is no question of ownership or lifetime management
  • Benefits from return value optimization
  • No heap allocation or indirection
// Unnecessarily complex
std::unique_ptr<Config> loadConfig(/*...*/) {
auto data = /*...*/;
if (some_condition) {
return std::make_unique<Config>(data);
}
return nullptr;
}
// Simple value semantics
std::optional<Config> loadConfig(/*...*/) {
auto data = /*...*/;
if (some_condition) {
return Config{data};
}
return std::nullopt;
}

Instead of out-parameters with bool return

Section titled “Instead of out-parameters with bool return”
// Awkward API - multiple parameters for one logical return
bool tryGetValue(int key, std::string& result) {
if (auto it = map.find(key); it != map.end()) {
result = it->second;
return true;
}
return false;
}
// Clear single return value
std::optional<std::string> getValue(int key) {
if (auto it = map.find(key); it != map.end()) {
return it->second;
}
return std::nullopt;
}

Consider alternatives when:

  • The type is very large (significant size overhead). Be aware that std::optional has the size of T also when ‘empty’.
  • You actually need ownership semantics
  • Performance critical code where the size overhead matters