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()
{
}
你可以试试
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_t
、std::is_arithmetic
和 std::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
}
所以基本上,我现在有一个非常基本的通用 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 中的任何内容都不匹配。
我在这里找到了类似的
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()
{
}
你可以试试
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_t
、std::is_arithmetic
和 std::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
}