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

时间:2018-10-13 21:41:14

标签: c++ templates c++14 sfinae overloading

我正在尝试实现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
    }
}

这里的想法是,如果我将charintfloat等传递给我的processImpl()函数,则应使用第一个重载;但是,事实并非如此。第二次重载似乎总是被使用,对于我可能做错的事情,我完全一无所知。我想它确实与我使用std::enable_if的方式有关

3 个答案:

答案 0 :(得分:3)

因此,要使其正常工作,应在两种情况下使用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
}

请注意第二种情况的否定。

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

ArchiveType& processImpl(Type&& type) {
    if constexpr(std::is_arithmetic_v<type>) {
        // implementation
    } else {
        // implementation
    }
}

答案 1 :(得分:3)

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

无特定顺序

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)>>

因为,如果用T进行的测试为真,则成为

 template <typename T, void>

这对于模板功能的签名没有意义。

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

 template <typename T, std::enable_if_t<(test with T)> * = nullptr>

成为

 template <typename T, void * = nullptr>

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

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)

现在此函数应该拦截算术值,但是...

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)

(b)传递一个中间函数,该函数发送附加的int值,并接收算术版本的int和通用变量的long;我的意思是

   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); }

通过这种方式,算术版本(恰好启用)精确接收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");
 }

答案 2 :(得分:2)

这应该可以解决问题

template<typename ArchiveType>
class OutputArchive {
    ...
    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
    }
};

Live sample

相关问题