C++可变参数类模板函数问题:Result_t的Ok函数推导失败排查
问题分析与解决方案
问题根源在于原Ok函数模板依赖推导Result_t的模板参数,但C++17及之前的标准中,模板别名(using定义)无法直接参与模板参数推导,导致编译器无法推导SuccessType;若直接写成无限制的泛型模板,又会允许任意std::variant调用,不符合需求。
核心思路是:利用Result_t的成功类型固定为第0个std::variant备选的特性,通过判断variant的索引是否为0实现状态检查,同时用SFINAE或C++20概念限制函数仅接受Result_t类型。
完整实现代码
#include <variant> #include <type_traits> #include <cassert> // void成功类型的占位符(std::variant无法直接存储void) struct VoidSuccess {}; // 通用Result_t定义:第0个模板参数为成功类型,其余为错误类型 template <typename Success, typename... Errors> using Result_t = std::variant<Success, Errors...>; // void成功类型的特化 template <typename... Errors> using Result_t<void, Errors...> = std::variant<VoidSuccess, Errors...>; // 辅助模板:判断类型是否为Result_t实例 template <typename T> struct is_result : std::false_type {}; template <typename Success, typename... Errors> struct is_result<Result_t<Success, Errors...>> : std::true_type {}; template <typename T> constexpr bool is_result_v = is_result<T>::value; // Ok状态判断函数:仅接受Result_t类型 template <typename Result> std::enable_if_t<is_result_v<Result>, bool> Ok(const Result& result) { // 成功类型是variant的第0个备选,直接判断索引即可 return result.index() == 0; } // 辅助函数:创建普通类型的成功结果 template <typename Success, typename... Errors> Result_t<Success, Errors...> Ok(Success&& val) { return Result_t<Success, Errors...>(std::forward<Success>(val)); } // 辅助函数:创建void类型的成功结果 template <typename... Errors> Result_t<void, Errors...> Ok() { return Result_t<void, Errors...>(VoidSuccess{}); } int main() { // 普通成功场景 auto ok_res = Ok<int, std::string>(42); assert(Ok(ok_res) == true); // 普通错误场景 Result_t<int, std::string> err_res = std::string("failed"); assert(Ok(err_res) == false); // void成功场景 auto void_ok = Ok<std::string>(); assert(Ok(void_ok) == true); // void错误场景 Result_t<void, std::string> void_err = std::string("void failed"); assert(Ok(void_err) == false); return 0; }
关键细节说明
- VoidSuccess占位符:由于
std::variant无法直接存储void,用空结构体作为void成功类型的替代。 - is_result辅助模板:通过特化识别
Result_t类型,结合std::enable_if_t实现SFINAE,确保Ok函数仅接受Result_t实例,避免过度泛化。 - 索引判断逻辑:直接利用
std::variant::index()检查是否为第0个备选类型,无需推导成功类型,彻底解决模板参数推导失败的问题。 - C++20简化方案:若使用C++20,可改用概念替代SFINAE,代码更简洁:
template <typename T> concept IsResult = is_result_v<T>; template <IsResult Result> bool Ok(const Result& result) { return result.index() == 0; }
内容的提问来源于stack exchange,提问作者MHebes
相关产品推荐
相关产品推荐

