So basically, I have a very basic generic class for now, currently testing the type_traits header. I am currently trying to make a function to work with certain types i.e arithmetic ones for now.
#include <type_traits>
template <typename T> class Test {
public:
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
The function works perfectly and for arithmetic types only.
But I like to keep my classes tidy and only have them have prototypes, while the function implementations are outside of the class.
With standard templates i.e
void test();
template <typename T> void Test<T>::test() {}
It is simple and I know how to, but I have no clue how to declare the implementation outside of the class with "std::enable_if
" and every attempt I have made, during compilation says that that the prototype does not match any in the class.
I have managed to find a similar question here but the class there is standard and not generic.
PS. I am using MinGW-w64 with -std=c++17
You need one set of template parameters for the class template and one separate set of template parameters for the member function template. You need to repeat the entire complicated return type, since it's part of the function template signature. And note you cannot repeat the default argument =T
, or the compiler will think you're trying to define it twice (without checking whether or not the new definition is identical).
template <typename T> template <typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type
Test<T>::print()
{
// Implementation here.
}
By the way, you're using the "long way" of writing the type, as was needed in C++11. But C++14 introduced a std::enable_if_t
shortcut, and C++17 introduced a std::is_arithmetic_v
shortcut. So if you're using C++17, you can also write the type
typename std::enable_if<std::is_arithmetic<U>::value>::type
as just
std::enable_if_t<std::is_arithmetic_v<U>>
If you put the enable_if
in the default template parameter, which is imo nicer anyway, the out-of-class definition becomes a bit easier:
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
}
You can try with
template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
{ /* do something */ }
The following is a full working example
#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
}
Off Topic 1
Observe that your solution can be hijacked in this way
Test<void>{}.print<int>();
To avoid this problem you could impose that T
is equal to 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()
{ }
};
Off Topic 2
As you can see, you have to repeat the SFINAE part (std::enable_if_t
, std::is_arithmetic
and std::is_same
).
Taking in count that you have to repeat the implementation in an header, I don't think (IMHO) that to write the implementation of template classes outside the body of the class is a great idea.
Since you haven't posted what you attempted I can't tell you where you went wrong. But this is how you would implement the member function outside the class definition (although it still needs to be implemented in the header, so I don't think this is worth the trouble)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With