在类定义之外使用enable_if的函数实现

时间:2018-01-27 20:56:10

标签: c++ templates sfinae typetraits c++17

基本上,我现在有一个非常基本的泛型类,目前正在测试type_traits头。我目前正在尝试使用某些类型的函数,即目前的算术类型。

#include <type_traits>

template <typename T> class Test {
    public:
        template <typename U = T> 
        typename std::enable_if<std::is_arithmetic<U>::value>::type print();
}; 

该功能完美适用,仅适用于算术类型。

但我喜欢保持我的课堂整洁,只让他们有原型,而功能实现不在课堂上。

使用标准模板,即

void test();

template <typename T> void Test<T>::test() {}

这很简单,我知道怎么做,但我不知道如何用&#34; std::enable_if&#34;在类之外声明实现。在编译过程中,我所做的每一次尝试都表明原型与类中的任何原型都不匹配。

我设法在这里找到了类似的question,但那里的类是标准的而不是通用的。

PS。我使用MinGW-w64和-std = c ++ 17

6 个答案:

答案 0 :(得分:6)

您需要为类模板提供一组模板参数,为成员函数模板提供一组单独的模板参数。您需要重复整个复杂的返回类型,因为它是函数模板签名的一部分。请注意,您不能重复默认参数=T,否则编译器会认为您尝试定义它两次(不检查新定义是否相同)。

template <typename T> template <typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type
Test<T>::print()
{
    // Implementation here.
}

顺便说一下,你正在使用&#34;漫长的方式&#34;编写类型,如C ++ 11中所需。但是C ++ 14引入了std::enable_if_t快捷方式,而C ++ 17引入了std::is_arithmetic_v快捷方式。因此,如果你正在使用C ++ 17,你也可以编写类型

typename std::enable_if<std::is_arithmetic<U>::value>::type

只是

std::enable_if_t<std::is_arithmetic_v<U>>

答案 1 :(得分:4)

您可以尝试使用

template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
 { /* do something */ }

以下是一个完整的工作示例

#include <iostream>
#include <type_traits>

template <typename T> class Test
 {
   public:
      template <typename U = T>
      std::enable_if_t<std::is_arithmetic<U>::value> print();
 }; 

template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
 { std::cout << "test!" << std::endl; }

int main ()
 {
   Test<int>  ti;
   Test<void> tv;

   ti.print();   // compile
   //tv.print(); // compilation error
 }

关闭主题1

观察您的解决方案可能以这种方式被劫持

Test<void>{}.print<int>(); 

要避免此问题,您可以强制T等于U

template <typename T> class Test
 {
   public:
      template <typename U = T>
      std::enable_if_t<   std::is_arithmetic<U>::value
                       && std::is_same<T, U>::value> print()
       { }
 }; 

关闭主题2

如您所见,您必须重复SFINAE部分(std::enable_if_tstd::is_arithmeticstd::is_same)。

考虑到你必须在标题中重复实现,我不认为(恕我直言)在类的主体之外编写模板类的实现是一个好主意。

答案 2 :(得分:1)

由于你还没有发布你尝试的内容,我无法告诉你哪里出错了。但是这就是你如何在类定义之外实现成员函数(虽然它仍然需要implemented in the header,所以我认为这不值得麻烦)

template <typename T> class Test {
    public:
        template <typename U = T>
        typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};

template <typename T>  // class template parameter
template <typename U>  // function template parameter
inline typename std::enable_if<std::is_arithmetic<U>::value>::type Test<T>::print()
{
}

Live demo

答案 3 :(得分:1)

如果你将enable_if放在默认的模板参数中,无论如何都要更好,那么类外定义会变得容易一些:

template<typename T>
struct Test
{
    template <typename S = T
            , typename = typename std::enable_if<std::is_arithmetic<S>::value>::type >
    void print();
};

template<typename T>
template<typename S, typename>
void Test<T>::print()
{
    //some code
}

答案 4 :(得分:0)

如果您需要额外的模板参数U,正如其他答案所解释的那样,正确的语法是

template<typename T>
struct test
{
    template<typename U>
    ... a_method(...);
};

template<typename T> 
template<typename U>
... test<T>::a_method(...)
{
   ...
}

然而,在您的特殊情况下,如果您只需要检查T类型的某些属性,这实际上是一个额外的复杂功能。 U类型的介绍是“人为的”,仅在此处,因为SFINAE

恕我直言,使用if constexpr

更优雅,更简单
#include <iostream>
#include <type_traits>

template <typename T>
class Test
{
 public:
  void print();
};

template <typename T>
void Test<T>::print()
{
  if constexpr (std::is_arithmetic_v<T>)
  {
    std::cout << "\nOk T is arithmetic";
    // ... your implementation here ...
  }
  else
  {
    // throw an exception or do what ever you want,
    // here a compile-time error
    static_assert(!std::is_same_v<T, T>, "not implemented yet...");
  }
}
main()
{
  Test<int> t;
  t.print();

  Test<void> t2;
  // t2.print(); <- will generate a compile time error
}

答案 5 :(得分:-1)

template<typename T>
struct test
{
    template<typename U = T>
    typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
template<typename T> template<typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type test<T>::print()
{
}
void foo()
{
    test<int> t;
    t.print();
    test<void*> u;
    u.print();
}