C++实现Optional类型时如何在模板参数支持特定运算符时启用对应函数
实现方案
核心逻辑基于C的SFINAE(替换失败不是错误)机制实现编译期运算符合法性校验,同时可以很方便地复用到所有C标准运算符上。
步骤1:实现通用的运算符检测元函数
先实现一套可复用的检测工具,不需要为每个运算符单独写判断逻辑:
#include <type_traits> // C++11 可自行实现 void_t,C++17及以上可直接用 std::void_t template <typename...> using void_t = void; // 通用检测器:判断指定操作是否支持 template <typename Void, template <typename...> typename Op, typename... Args> struct is_op_valid : std::false_type {}; template <template <typename...> typename Op, typename... Args> struct is_op_valid<void_t<Op<Args...>>, Op, Args...> : std::true_type {}; template <template <typename...> typename Op, typename... Args> constexpr bool is_op_valid_v = is_op_valid<void, Op, Args...>::value; // 针对operator+的操作定义,后续其他运算符只要新增对应定义即可 template <typename T, typename U> using plus_op_t = decltype(std::declval<T>() + std::declval<U>());
步骤2:填充/* some magic */位置的校验逻辑
方案A:兼容你原有模板定义的写法
校验逻辑需要满足两个条件:
- 包装类型
T和入参Other支持operator+运算 - 运算结果可以转换为你指定的返回类型
R
template <typename R, typename Other, typename std::enable_if< is_op_valid_v<plus_op_t, T, Other> && std::is_convertible_v<plus_op_t<T, Other>, R> >::type* = nullptr> Optional<R> operator+(Other other) { if (stdOptional) return Optional<R>(stdOptional.value() + other); return nullopt_t{}; }
方案B:更易用的优化写法(自动推导返回值,无需手动指定R)
不用把R作为模板参数传入,自动根据运算结果推导返回的Optional包装类型:
// 删除原来的声明模板,直接用以下实现 template <typename Other, typename std::enable_if< is_op_valid_v<plus_op_t, T, Other> >::type* = nullptr> auto operator+(Other other) -> Optional<std::decay_t<plus_op_t<T, Other>>> { if (stdOptional) return stdOptional.value() + other; return nullopt_t{}; }
使用时无需手动指定返回类型,直接用auto接收即可:
Optional<int> intOp{1}; auto result = intOp + 1; // 自动推导为Optional<int>类型
步骤3:复用到其他运算符
只需要新增对应运算符的操作定义即可,不需要修改通用检测逻辑:
// 检测operator- template <typename T, typename U> using minus_op_t = decltype(std::declval<T>() - std::declval<U>()); // 检测operator* template <typename T, typename U> using multiply_op_t = decltype(std::declval<T>() * std::declval<U>()); // 检测operator== template <typename T, typename U> using equal_op_t = decltype(std::declval<T>() == std::declval<U>()); // 其他运算符以此类推
内容的提问来源于stack exchange,提问作者Gian Laager
相关产品推荐
相关产品推荐

