This is just a question about style: I don't like the way of C++ for template metaprogramming that requires you to use the return type or add an extra dummy argument for the tricks with SFINAE. So, the idea I came up with is to put the SFINAE thing in the template arguments definition itself, like this:
#include <iostream>
#include <boost/type_traits/is_array.hpp>
#include <boost/utility/enable_if.hpp>
using namespace std;
template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){
cout<<"This is for arrays"<<endl;
}
template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){
cout<<"This is for NON arrays"<<endl;
}
int main() {
asd<int>();
asd<int[]>();
}
This example make g++ complain:
../src/afg.cpp:10:97: error: redefinition of ‘template void asd()’
SFINAE there itself works, because if I delete for example the one with disable_if
, the compiler error is:
../src/afg.cpp:15:12: error: no matching function for call to ‘asd()’
Which is what I want.
So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list?
EDIT: This is in the end what I'm going to try in the real code:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){
cout<<"This is for arrays"<<endl;
}
template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){
cout<<"This is for NON arrays"<<endl;
}
int main() {
asd<int[]>();
asd<int>();
}
I use c++0x stuff instead of boost because as long as I need c++0x for using defaults of template arguments, I see no reason to use boost, which is its precursor.
Well, I generally use these macros to make enable_if constructs a lot cleaner(they even work in most C++03 compilers):
#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type
Then you would define your function like this:
template <typename T >
FUNCTION_REQUIRES(is_array<T>)
(void) asd(){
cout<<"This is for arrays"<<endl;
}
template <typename T >
FUNCTION_REQUIRES(EXCLUDE(is_array<T>))
(void) asd(){
cout<<"This is for NON arrays"<<endl;
}
The only thing is, you need to put parenthesis around the return type. If you forget them, the compiler will say something like 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' is undefined.
Since C++11 made it possible, I only ever use enable_if
(or conversely disable_if
) inside the template arguments, the way you're doing. If/when there are several overloads, then I use dummy, defaulted template arguments which makes the template parameter lists differ in arity. So to reuse your example that would be:
template<
typename T
, typename B = typename boost::enable_if<
boost::is_array<T>
>::type
>
void asd() {
cout << "This is for arrays" << endl;
}
template<
typename T
, typename B = typename boost::disable_if<
boost::is_array<T>
>::type
, typename = void
>
void asd() {
cout << "This is for arrays" << endl;
}
Another alternative to not messing the return type (that is not available in some cases, e.g. conversion operators) that has existed since C++03 is to use default arguments:
template<typename T>
void
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr);
I don't use this form as I dislike 'messing' with the argument types just as much as with the return type, and for consistency reasons (since that's not doable in all cases).
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