std::enable_if 和 std::is_arithmetic 作为模板参数的问题

Reb*_*orn 5 c++ templates overloading sfinae c++14

我正在尝试实现一个OutputArchive模板类,它有一个模板化的函数processImpl()。看起来像这样:

template<typename ArchiveType>
class OutputArchive {
    ...

    template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>>> inline
    ArchiveType& processImpl(Type&& type) {
        // Implementation
    }

    template<typename Type, typename = void> inline
    ArchiveType& processImpl(Type&& type) {
        // Implementation
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的想法是,如果我通过在一charintfloat,等我的processImpl()功能,第一个重载应使用; 然而,情况并非如此。第二个重载似乎总是被使用,我完全不知道我可能做错了什么。我想它确实有一些跟我使用的方式std::enable_if,虽然

Igo*_*gor 6

因此,要使其工作,您应该在 2 种情况下使用 std::enable_if。我将展示一个返回类型的例子,但使用模板参数也可以。

template<typename Type> inline
typename std::enable_if_t<std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
    // Implementation
}

template<typename Type> inline
typename std::enable_if_t<!std::is_arithmetic_v<Type>, ArchiveType&> processImpl(Type&& type) {
    // Implementation
}
Run Code Online (Sandbox Code Playgroud)

注意第二种情况的否定。

但是从 C++17 开始,更好的方法是使用 constexpr:

ArchiveType& processImpl(Type&& type) {
    if constexpr(std::is_arithmetic_v<type>) {
        // implementation
    } else {
        // implementation
    }
}
Run Code Online (Sandbox Code Playgroud)


max*_*x66 6

您的代码中存在一些问题。

没有特别的顺序

1) 不是错误(我想)但是...使用typename std::enable_if<...>::type或,从 C++14 开始,std::enable_if_t<...>typename之前不需要使用std::enable_if_t

2)如果要std::enable_if在参数类型列表中使用,这个不行

 template <typename T, std::enable_if_t<(test with T)>>
Run Code Online (Sandbox Code Playgroud)

因为,如果测试T为真,则变为

 template <typename T, void>
Run Code Online (Sandbox Code Playgroud)

这作为模板函数的签名没有意义。

您可以 SFINAE 启用/禁用返回值(请参阅 Igor 或 Marek R 的答案),或者您可以改为编写

 template <typename T, std::enable_if_t<(test with T)> * = nullptr>
Run Code Online (Sandbox Code Playgroud)

变成

 template <typename T, void * = nullptr>
Run Code Online (Sandbox Code Playgroud)

并且有意义,作为签名,并且有效

3) 正如评论中指出的那样,你应该使用std::remove_reference,所以

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type)
Run Code Online (Sandbox Code Playgroud)

现在这个函数应该拦截算术值但是......

4) 前面的processImpl(),对于算术值,与另一个冲突,processImpl()因为在算术值的情况下,两个版本都匹配并且编译器无法选择一个。

我可以建议两种解决方案

(a) 通过 SFINAE 禁用算术情况下的第二个版本;我的意思是,写第二个如下

   template <typename Type,
             std::enable_if_t<false == std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
    ArchiveType & processImpl (Type && type)
Run Code Online (Sandbox Code Playgroud)

(b) 通过一个中间函数,该函数发送一个int附加值并接收int算术版本中的 along和泛型中的 a;我的意思是

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                  std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type, int)
    { /* ... */ }

   template <typename Type>
   ArchiveType & processImpl (Type && type, long)
    { /* ... */ }

   template <typename Type>
   ArchiveType & processImpl (Type && type)
    { return processImpl(type, 0); }
Run Code Online (Sandbox Code Playgroud)

这样算术版本,接收到一个int,比通用版本更受青睐(启用时);否则使用通用版本。

以下是基于 (b) 解决方案的完整工作 C++14 示例

#include <iostream>
#include <type_traits>

template <typename ArchiveType>
struct OutputArchive
 {
   ArchiveType  value {};

   template <typename Type,
             std::enable_if_t<std::is_arithmetic_v<
                std::remove_reference_t<Type>>> * = nullptr> inline
   ArchiveType & processImpl (Type && type, int)
    {
      std::cout << "--- processImpl aritmetic: " << type << std::endl;

      return value;
    }

   template <typename Type>
   ArchiveType & processImpl (Type && type, long)
    {
      std::cout << "--- processImpl generic: " << type << std::endl;

      return value;
    }

   template <typename Type>
   ArchiveType & processImpl (Type && type)
    { return processImpl(type, 0); }
 };

int main()
 {
   OutputArchive<int>  oa;

   long  l{2l};
   oa.processImpl(l);
   oa.processImpl(3);
   oa.processImpl("abc");
 }
Run Code Online (Sandbox Code Playgroud)