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.
Why std::optional is simpler
Section titled “Why std::optional is simpler”- It’s a value: there is no question of ownership or lifetime management
- Benefits from return value optimization
- No heap allocation or indirection
Common alternatives to avoid
Section titled “Common alternatives to avoid”Instead of unique_ptr for optional values
Section titled “Instead of unique_ptr for optional values”// Unnecessarily complexstd::unique_ptr<Config> loadConfig(/*...*/) { auto data = /*...*/; if (some_condition) { return std::make_unique<Config>(data); } return nullptr;}
// Simple value semanticsstd::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 returnbool 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 valuestd::optional<std::string> getValue(int key) { if (auto it = map.find(key); it != map.end()) { return it->second; } return std::nullopt;}When this guideline doesn’t apply
Section titled “When this guideline doesn’t apply”Consider alternatives when:
- The type is very large (significant size overhead). Be aware that std::optional has the size of
Talso when ‘empty’. - You actually need ownership semantics
- Performance critical code where the size overhead matters