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

如何创建支持两种回调签名的C++20重试包装模板函数

问题描述

我有一个执行器,它接收待执行的函数作为参数,支持两种回调签名(取决于回调是否需要session对象):

template<typename T, typename S>
T run(const std::function<T(Client&)>& f, S s);

template<typename T, typename S>
T run(const std::function<T(Client&, const S&)>& f, S s);

我想编写一个runWithRetry函数来包装这两个run,实现重试逻辑,但不想写两份几乎完全相同的代码。目前的重复实现如下:

template <typename T, typename S>
T runWithRetry(const std::function<T(Client&)>& f, S s) {
  for (;;) {
    try {
      return run(f, s);
    } catch (Retry &e) {}
  }
}

template <typename T, typename S>
T runWithRetry(const std::function<T(Client&, const S&)>& f, S s) {
  for (;;) {
    try {
      return run(f, s);
    } catch (Retry &e) {}
  }
}

我尝试将回调设为模板参数,但需要正确推导返回类型,不知道如何实现能兼容两种签名的return_type。


解决方案(C++20)

利用C++20的特性可以统一实现逻辑,无需重复代码,同时自动推导返回类型。以下是几种可行方式:

方法1:decltype(auto) + 编译期分支判断

直接用模板参数接受任意可调用对象,通过if constexpr结合requires子句在编译期判断回调签名,自动匹配对应的run函数,decltype(auto)会自动推导返回类型:

#include <functional>
#include <utility>

// 前置声明依赖类型
class Client;
class Retry;

// 原run函数前置声明
template<typename T, typename S>
T run(const std::function<T(Client&)>& f, S s);

template<typename T, typename S>
T run(const std::function<T(Client&, const S&)>& f, S s);

// 统一的重试包装函数
template <typename F, typename S>
decltype(auto) runWithRetry(F&& f, S&& s) {
  for (;;) {
    try {
      // 编译期判断回调是否支持带session的调用
      if constexpr (requires(F&& func, Client& c, const S& sess) {
        func(c, sess);
      }) {
        return run(std::forward<F>(f), std::forward<S>(s));
      } else {
        return run(std::forward<F>(f), std::forward<S>(s));
      }
    } catch (Retry&) {
      // 捕获重试异常,继续循环
      continue;
    }
  }
}

方法2:用概念(Concepts)明确约束回调类型

通过定义概念区分两种回调签名,再重载runWithRetry,代码可读性更强:

#include <functional>
#include <utility>

class Client;
class Retry;

// 定义概念:不需要session的回调
template <typename F, typename S>
concept NoSessionCallback = requires(F f, Client& c) {
  { f(c) } -> std::convertible_to<decltype(run(std::function{f}, std::declval<S>()))>;
};

// 定义概念:需要session的回调
template <typename F, typename S>
concept SessionCallback = requires(F f, Client& c, const S& s) {
  { f(c, s) } -> std::convertible_to<decltype(run(std::function{f}, std::declval<S>()))>;
};

// 原run函数前置声明
template<typename T, typename S>
T run(const std::function<T(Client&)>& f, S s);

template<typename T, typename S>
T run(const std::function<T(Client&, const S&)>& f, S s);

// 重载1:匹配不需要session的回调
template <NoSessionCallback<F, S> F, typename S>
decltype(auto) runWithRetry(F&& f, S&& s) {
  for (;;) {
    try {
      return run(std::forward<F>(f), std::forward<S>(s));
    } catch (Retry&) {}
  }
}

// 重载2:匹配需要session的回调
template <SessionCallback<F, S> F, typename S>
decltype(auto) runWithRetry(F&& f, S&& s) {
  for (;;) {
    try {
      return run(std::forward<F>(f), std::forward<S>(s));
    } catch (Retry&) {}
  }
}

两种方法都避免了重复编写重试逻辑,且无需手动实现return_type类,完全依赖C++20的编译期特性完成自动适配。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:40:33