【问题标题】:`enable_if()` to disable static member function declaration of templated class`enable_if()` 禁用模板类的静态成员函数声明
【发布时间】:2026-02-02 06:25:01
【问题描述】:

我正在尝试在模板类中声明函数,以便函数声明依赖于模板类型参数。

template<typename T>
struct Block
{
    static bool parse(int32_t index, 
                      const typename std::enable_if<std::is_class<T>::value, T>::type& value);

    static bool parse(int32_t index, 
                      typename std::enable_if<!std::is_class<T>::value, T>::type value);
....

};

所以我想将Block&lt;uint16_t&gt;Block&lt;std::string&gt;parse() 声明为:

bool parse(int32_t index, const std::string& value);
or
bool parse(int32_t index, uint16_t value);

但我收到错误:'enable_if' cannot be used to disable this declaration ...typename std::enable_if<!std::is_class<T>::value, T>::type value);

您能帮我正确声明函数吗?
谢谢。

【问题讨论】:

    标签: c++ templates sfinae


    【解决方案1】:

    Enable_if 仅适用于推断的上下文。在您的示例中,扣除是在类类型时间完成的。到函数时,T 已经知道,所以没有什么可以推导出来的。

    您可以制作一个多余的模板参数,将其默认类型设置为 T,然后推断。

    template<typename T>
    struct Block
    {
        // now parse has to deduce U
        template<typename U=T>
        static bool parse(int32_t index, 
                          typename std::enable_if<!std::is_class<U>::value, T>::type value);
    

    您的调用者永远不会知道parse 实际上有一个模板参数,但现在您可以在上面做所有花哨的事情。

    【讨论】:

    • noup,得到out-of-line definition...wandbox.org/permlink/yrjX19QW3wjaGPxX
    • 如果你把函数定义内联,它工作正常。 wandbox.org/permlink/XgzDCmKle33DuNvP 。您尝试在课堂之外定义它们的方式是错误的。签名不对齐。
    • 这就是我想将定义保留在外面的问题。我有几个不同的类,它们具有不同的解析器,但参数类型相同。喜欢:using BlockS1 = Block&lt;std::string&gt;; using BlockS2 = Block&lt;std::string&gt;。这个块实际上有一个额外的typenames 来区分 BlockS1 和 BlockS2,但解析器仅基于第一个参数。这就是为什么我要专门研究解析器函数。抱歉,我试图使示例尽可能简单,并删除了其他所有内容。我更新了示例以使其更清晰wandbox.org/permlink/aep3akqdtrkpxAju
    • @user3124812 我只会专门化这个类而不是解析。
    【解决方案2】:
    1. 没有 SFINAE 的替代解决方案是使用标签调度:

      template<typename S>
      static bool parse(int32_t index, S&& value) {
          return parse_impl(index, value, std::is_class<T>{});
      }
      
      private: 
      static bool parse_impl(int32_t index, const T& value, std::true_type);
      static bool parse_impl(int32_t index, T value, std::false_type);
      

      请注意,在此解决方案中,parse() 接受任何类型的 S,如果 S 不能转换为 T,则失败将发生在 parse() 本身内部。如果parse() 本身用于 SFINAE,这可能为时已晚。例如以下模板:

      template<class S, class T, typename = decltype(S::parse(0, std::declval<T>()))>
      

      将导致原始parse() 的软失败(这不是错误)和建议的标签分派版本的硬失败(这是一个编译错误)。感谢 xaxxon 指出这一差异。

    2. 在带有概念的 C++20 中,requires 将简化事情:

      static bool parse(int32_t index, const T& value)
      requires std::is_class_v<T>
      { ... }
      
      static bool parse(int32_t index, T value)
      requires !std::is_class_v<T>
      { ... }
      

    【讨论】:

    • 你的函数需要一个 S,然后调用一个需要 T 的函数。
    • @xaxxon,这有什么问题?
    • 因为在过程中失败太晚了,其他事情可能依赖于 parse 是否有效以使用其他类型 T 调用。为什么要从问题指定的内容更改 parse 的原型需要吗?
    • @xaxxon,如果parse() 必须提前失败,那么是的,这是一个好点。我会在答案中注明。
    • @xaxxon,我已经更新了我的答案。我希望我理解了您所说的“为时已晚”的意思。