C++禁用异常时,如何消除多std::optional赋值的重复代码
如何检查多个不同函数返回的std::optional是否都包含有效值?
假设我们有多个返回std::optional的函数,需要将它们的结果赋值给结构体成员,只要其中任何一个函数返回std::nullopt,就返回std::nullopt而非完整结构体。原始实现存在大量重复的if-else代码,现有异常方案和tuple方案都存在缺陷,如何在不使用异常的前提下实现简洁且高效的逻辑?
原始代码如下:
#include <string> #include <optional> #include <charconv> std::optional<int> get_1(const std::string& s) { return s == "A" ? 1 : std::optional<int>{}; } std::optional<const char*> get_2(const std::string& s) { return s == "A" ? "2" : std::optional<const char*>{}; } std::optional<double> get_3(const std::string& s) { return s == "A" ? 3.0 : std::optional<double>{}; } std::optional<int64_t> get_4(const std::string& s) { return s == "A" ? -10000000000 : std::optional<int64_t>{}; } std::optional<std::string> get_5(const std::string& s) { return s == "A" ? "5" : std::optional<std::string>{}; } // ..... more functions like that struct S { int a1; const char* a2; double a3; int64_t a4; std::string a5; private: int AGR_INIT_IS_NOT_ALLOWED{0}; }; std::optional<S> getS() { S s; // Now I'd like to return std::nullopt if any of the following calls return std::nullopt // The naive way to do that is smth like that if (auto val = get_1("A"); val) { s.a1 = *val; } else { return std::nullopt; } if (auto val = get_2("A"); val) { s.a2 = *val; } else { return std::nullopt; } if (auto val = get_3("A"); val) { s.a3 = *val; } else { return std::nullopt; } if (auto val = get_4("A"); val) { s.a4 = *val; } else { return std::nullopt; } if (auto val = get_5("A"); val) { s.a5 = *val; } else { return std::nullopt; } return s; } int main() { return !getS(); }
现有两种方案的缺陷:
- 异常方案:需要修改所有
get_*函数改为抛出异常,且项目禁止使用异常,不可行。 - Tuple方案:会执行所有
get_*函数(即使第一个已经返回nullopt),且赋值时需要通过std::get索引,不够便捷。
解决方案:封装辅助模板函数
我们可以编写一个通用的模板函数,负责执行目标函数、检查std::optional有效性并完成赋值,同时支持短路求值——一旦某个步骤失败,后续函数不会被调用。
完整代码如下:
#include <string> #include <optional> #include <charconv> std::optional<int> get_1(const std::string& s) { return s == "A" ? 1 : std::optional<int>{}; } std::optional<const char*> get_2(const std::string& s) { return s == "A" ? "2" : std::optional<const char*>{}; } std::optional<double> get_3(const std::string& s) { return s == "A" ? 3.0 : std::optional<double>{}; } std::optional<int64_t> get_4(const std::string& s) { return s == "A" ? -10000000000 : std::optional<int64_t>{}; } std::optional<std::string> get_5(const std::string& s) { return s == "A" ? "5" : std::optional<std::string>{}; } // ..... more functions like that struct S { int a1; const char* a2; double a3; int64_t a4; std::string a5; private: int AGR_INIT_IS_NOT_ALLOWED{0}; }; // 辅助模板函数:执行函数获取optional,赋值给目标引用,失败返回false template<typename Func, typename T> bool assign_if_valid(Func&& func, T& target) { if (auto opt = func()) { target = *opt; return true; } return false; } std::optional<S> getS() { S s; // 短路求值:只要有一个assign失败,立即返回nullopt if (!assign_if_valid([](){ return get_1("A"); }, s.a1) || !assign_if_valid([](){ return get_2("A"); }, s.a2) || !assign_if_valid([](){ return get_3("A"); }, s.a3) || !assign_if_valid([](){ return get_4("A"); }, s.a4) || !assign_if_valid([](){ return get_5("A"); }, s.a5)) { return std::nullopt; } return s; } int main() { return !getS(); }
方案优势
- 短路求值:和异常方案逻辑一致,一旦某个
get_*返回nullopt,后续函数不会执行,避免不必要的计算。 - 无代码重复:将重复的检查和赋值逻辑封装到模板函数中,
getS函数代码简洁清晰。 - 无需修改原有函数:保留
get_*函数返回std::optional的原有接口,无需重构。 - 通用性强:模板函数支持任意返回
std::optional的函数和任意类型的目标变量,扩展性好。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

