In this example, I'm executing a function based on if that function exists in a class.
#include<type_traits>
#include<iostream>
#define TEMPLATE_TYPE FN_TEMPLATE
#define FN_TEMPLATE 0
#define CLASS_TEMPLATE 1
#define CLASS_FN_TEMPLATE 2
/*! The template `has_begin<T,RET_TYPE>` exports a
boolean constant `value that is true iff `T` provides
`RET_TYPE T::begin() const`
*/
template< typename CLASS>
struct has_begin
{
/* SFINAE operator-has-correct-sig :) */
template<typename A_CLASS>
static std::true_type test(void(A_CLASS::*)()) {
return std::true_type();
}
/* SFINAE operator-exists :) */
template <typename A>
static decltype(test(&A::begin))
test(decltype(&A::begin), void *) {
/* Operator exists. What about sig? */
typedef decltype(test(&A::begin)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<CLASS>(0, 0)) type;
static const bool value = type::value; /* Which is it? */
};
struct A
{
void begin()
{
std::cout << "HI" << std::endl;
}
};
struct B
{
};
#if TEMPLATE_TYPE == FN_TEMPLATE
template<typename T>
typename std::enable_if<has_begin<T>::value>::type
call()
{
T().begin();
}
template<typename T>
typename std::enable_if<!has_begin<T>::value>::type
call()
{
}
#elif TEMPLATE_TYPE == CLASS_TEMPLATE
template<typename T>
struct test
{
static
typename std::enable_if<has_begin<T>::value>::type
call()
{
T().begin();
}
static
typename std::enable_if<!has_begin<T>::value>::type
call()
{
}
};
#elif TEMPLATE_TYPE == CLASS_FN_TEMPLATE
template<typename A>
struct test
{
template<typename T>
static
typename std::enable_if<has_begin<T>::value>::type
call()
{
T().begin();
}
template<typename T>
static
typename std::enable_if<!has_begin<T>::value>::type
call()
{
}
};
#endif
int main()
{
#if TEMPLATE_TYPE == FN_TEMPLATE
call<A>();
call<B>();
#elif TEMPLATE_TYPE == CLASS_TEMPLATE
test<A>::call();
test<B>::call();
#elif TEMPLATE_TYPE == CLASS_FN_TEMPLATE
test<void>::call<A>();
test<void>::call<B>();
#endif
return 0;
}
If you set TEMPLATE_TYPE to FN_TEMPLATE or CLASS_FN_TEMPLATE all works as expected, the programme compiles outputting "HI" which is the command inside of A::begin(). B::begin() doesn't get called because there is no B::begin() member function.
However, if TEMPLATE_TYPE is set to CLASS_TEMPLATE, this fails to compile citing that both test<X>::call() functions have been instantiated.
Why does SFINAE not work in this case?
Aucun commentaire:
Enregistrer un commentaire