C++17自定义std::expected实现条件编译时警告
实现编译时警告:当on_success传入非void返回值的函数时触发
问题描述
我正在为C++17实现类似std::expected的自定义类,其中on_success方法仅在持有成功值时调用传入的函数,持有错误则不执行,方法返回自身。我期望传入的函数返回void(比如日志操作),希望当函数返回非void时触发编译时警告。
尝试过两种方式均失败:
- 使用
#if预处理器指令结合类型判断,但因表达式中的逗号导致编译报错; - 使用
if constexpr,但无论条件真假都会生成警告。
相关代码如下:
#define NOT_INVOCABLE_MESSAGE "function must be invocable on the given type" template <class Success, class Error> class Expected { public: template<typename F> Expected<Success, Error>& on_success(F &&f) & noexcept { static_assert(std::is_invocable_v<F, Success &>, NOT_INVOCABLE_MESSAGE); // IDE highlight on the first comma: "Expected end of line in preprocessor expression" // compiler error: "error: operator '&' has no right operand" #if !std::is_same_v<void, std::invoke_result_t<F, Success &>> #warning "return value of the given function will be ignored" #endif // warning gets generated even when condition is false if constexpr (!std::is_same_v<void, std::invoke_result_t<F, Success &>>) { #warning "return value of the given function will be ignored" } if (_hasValue) std::forward<F>(f)(_success); return *this; } private: union { Success _success; Error _error; }; bool _hasValue; };
解决方案
方法一:利用if constexpr结合_Pragma指令
_Pragma是标准C++支持的预处理器指令写法,可以在if constexpr的条件分支中使用——因为if constexpr的假分支不会被实例化,所以分支内的_Pragma也不会被执行。这样就能实现仅当函数返回非void时触发警告。
修改后的代码如下:
#define NOT_INVOCABLE_MESSAGE "function must be invocable on the given type" // 跨编译器的警告宏 #ifdef _MSC_VER #define NON_VOID_RETURN_WARN _Pragma("message(\"return value of the given function will be ignored\")") #else #define NON_VOID_RETURN_WARN _Pragma("warning \"return value of the given function will be ignored\"") #endif template <class Success, class Error> class Expected { public: template<typename F> Expected<Success, Error>& on_success(F &&f) & noexcept { static_assert(std::is_invocable_v<F, Success &>, NOT_INVOCABLE_MESSAGE); using ReturnType = std::invoke_result_t<F, Success &>; if constexpr (!std::is_same_v<void, ReturnType>) { NON_VOID_RETURN_WARN; } if (_hasValue) std::forward<F>(f)(_success); return *this; } private: union { Success _success; Error _error; }; bool _hasValue; };
方法二:模板特化配合编译器属性
通过定义辅助模板的特化版本,仅在需要警告的场景下实例化带有警告属性的结构,从而触发编译提示。这种方式更灵活,适合需要自定义警告行为的场景。
代码示例:
#define NOT_INVOCABLE_MESSAGE "function must be invocable on the given type" // 辅助模板:控制是否触发警告 template<bool ShouldWarn> struct WarnOnNonVoidReturn; // 特化:无需警告的情况 template<> struct WarnOnNonVoidReturn<false> { static void warn() {} }; // 特化:需要警告的情况(GCC/Clang) template<> struct WarnOnNonVoidReturn<true> { [[gnu::warning("return value of the given function will be ignored")]] static void warn() {} }; // 如果是MSVC,替换为以下特化: // template<> // struct WarnOnNonVoidReturn<true> { // [[deprecated("return value of the given function will be ignored")]] // static void warn() {} // }; template <class Success, class Error> class Expected { public: template<typename F> Expected<Success, Error>& on_success(F &&f) & noexcept { static_assert(std::is_invocable_v<F, Success &>, NOT_INVOCABLE_MESSAGE); using ReturnType = std::invoke_result_t<F, Success &>; constexpr bool shouldWarn = !std::is_same_v<void, ReturnType>; WarnOnNonVoidReturn<shouldWarn>::warn(); if (_hasValue) std::forward<F>(f)(_success); return *this; } private: union { Success _success; Error _error; }; bool _hasValue; };
说明
- 方法一中的
_Pragma是标准C++特性,但内部的警告指令是编译器特定的:GCC/Clang使用warning,MSVC使用message; - 方法二中的
[[gnu::warning]]是GCC/Clang专属属性,MSVC可使用[[deprecated]]替代,同样能达到提示效果; - 两种方法都依赖C17的
if constexpr或模板特化的特性,符合你的C17环境要求。
内容的提问来源于stack exchange,提问作者Mikhail Tsaritsyn
相关产品推荐
相关产品推荐

