You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
}

方案优势

  1. 短路求值:和异常方案逻辑一致,一旦某个get_*返回nullopt,后续函数不会执行,避免不必要的计算。
  2. 无代码重复:将重复的检查和赋值逻辑封装到模板函数中,getS函数代码简洁清晰。
  3. 无需修改原有函数:保留get_*函数返回std::optional的原有接口,无需重构。
  4. 通用性强:模板函数支持任意返回std::optional的函数和任意类型的目标变量,扩展性好。

内容的提问来源于stack exchange,提问作者Dmitry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:12:13