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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:06:25