I am trying to implement an OutputArchive template class, which has a templated function processImpl(). That looks like this:
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
}
}
The idea here is that if I pass in a char, int, float, etc to my processImpl() function, the first overload should be used; however, that is not the case. The second overload seems to always be used and I am completely clueless as to what I could be doing wrong. I imagine it does have something to do with the way I am using std::enable_if though
So to make it work you should use std::enable_if for 2 cases. I will show an example for return type, but using template parameter would also work.
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
}
Note the negation in the second case.
But as of C++17 a better way would be to use constexpr:
ArchiveType& processImpl(Type&& type) {
if constexpr(std::is_arithmetic_v<type>) {
// implementation
} else {
// implementation
}
}
There are some problem in your code.
In no particular order
1) not an error (I suppose) but... use typename std::enable_if<...>::type or, starting from C++14, std::enable_if_t<...>; no need to use typename before std::enable_if_t
2) if you want to use std::enable_if in the list of parameter types, this one doesn't work
template <typename T, std::enable_if_t<(test with T)>>
because, if the test with T is true, become
template <typename T, void>
that doesn't make sense as signature for a template function.
You can SFINAE enable/disable the return value (see the Igor's or the Marek R's answers) or you can write, instead
template <typename T, std::enable_if_t<(test with T)> * = nullptr>
that become
template <typename T, void * = nullptr>
and make sense, as signature, and works
3) as pointed by in comments, you should use std::remove_reference, so
template <typename Type,
std::enable_if_t<std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type)
now this function should intercept arithmetic values but...
4) the preceding processImpl(), for arithmetic values, collide with the other processImpl() because, in case of arithmetic values, both versions matches and the compiler can't select one over another.
I can suggest two solutions
(a) disable, through SFINAE, the second version in arithmetic cases; I mean, write the second one as follows
template <typename Type,
std::enable_if_t<false == std::is_arithmetic_v<
std::remove_reference_t<Type>>> * = nullptr> inline
ArchiveType & processImpl (Type && type)
(b) pass through an intermediate function that send an additional int value and receive an int in the arithmetic version and a long in the generic; I mean something as
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); }
This way the arithmetic version, receiving exactly an int, is preferred (when enabled) over the generic version; the generic version is used otherwise.
The following is a full working C++14 example based over the (b) solution
#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");
}
This should do the trick
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.
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