enable_if 在 class 定义之外的函数实现

function implementation with enable_if outside of class definition

所以基本上,我现在有一个非常基本的通用 class,目前正在测试 type_traits header。我目前正在尝试制作一个函数来处理某些类型,即现在的算术类型。

#include <type_traits>

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

该函数完美运行且仅适用于算术类型。

但我喜欢保持我的 class 整洁,只有它们有原型,而函数实现在 class 之外。

使用标准模板,即

void test();

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

这很简单,我知道怎么做,但我不知道如何用“std::enable_if”在 class 之外声明实现,我所做的每一次尝试,在编译过程中都说原型与 class 中的任何内容都不匹配。

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

PS。我正在使用带有 -std=c++17

的 MinGW-w64

由于您还没有发布您尝试过的内容,所以我无法告诉您哪里出了问题。但这是在 class 定义之外实现成员函数的方式(尽管它仍然需要是 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

你可以试试

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

考虑到您必须在 header 中重复实现,我不认为(恕我直言)在 [=35= 之外编写模板 classes 的实现] 的 class 是个好主意。

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

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

顺便说一句,您正在使用 "long way" 编写类型,这在 C++11 中是需要的。但是 C++14 引入了 std::enable_if_t 快捷方式,而 C++17 引入了 std::is_arithmetic_v 快捷方式。所以如果你使用的是 C++17,你也可以写成 type

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

正如

std::enable_if_t<std::is_arithmetic_v<U>>
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();
}

如果你把 enable_if 放在默认的模板参数中,无论如何这样更好,class 之外的定义会变得更容易一些:

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
}

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

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

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

但是在您的特殊情况下,如果您只需要检查 T 类型的某些属性,这确实是一个额外的复杂问题。 U 类型的介绍是 "artificial" 并且只在这里因为 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
}