As a follow-up to my previous question, I am trying to detect the existence of a template function that requires explicit specialization.
My current working code detects non-template functions (thanks to DyP's help), provided they take at least one parameter so that dependent name lookup can be used:
// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1
namespace foo {
#if ENABLE_FOO_BAR
int bar(int);
#endif
}
namespace feature_test {
namespace detail {
using namespace foo;
template<typename T> decltype(bar(std::declval<T>())) test(int);
template<typename> void test(...);
}
static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}
(the ENABLE_FOO_BAR
macro is just for testing purpose, in my real code I don't have such a macro available otherwise I wouldn't be using SFINAE)
This also works perfectly with template functions when their template arguments can automatically be deduced by the compiler:
namespace foo {
#if ENABLE_FOO_BAR
template<typename T> int bar(T);
#endif
}
However when I try to detect a template function that requires explicit specialization, the static_assert
kicks in when foo::bar()
exists:
namespace foo {
#if ENABLE_FOO_BAR
template<typename T, typename U> T bar(U);
#endif
}
//...
// error: static assertion failed: something went wrong
Obviously the compiler can't deduce the template arguments of bar()
so the detection fails. I tried to fix it by explicitly specializing the call:
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
// explicit specialization ^^^^^^^^
This works fine when foo::bar()
exists (the function is correctly detected) but now all hell breaks loose when foo::bar()
doesn't exist:
error: ‘bar’ was not declared in this scope
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
^
error: expected primary-expression before ‘int’
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
^
// lots of meaningless errors that derive from the first two
It seems my attempt at explicit specialization failed because the compiler doesn't know that bar
is a template.
I'll spare you everything I tried to fix this and get straight to the point: how can I detect the existence of a function such as template<typename T, typename U> T bar(U);
that requires explicit specialization in order to be instantiated?
One of the primary uses of SFINAE can be found through enable_if expressions. enable_if is a set of tools, available in the Standard Library since C++11, that internally use SFINAE. They allow to include or exclude overloads from possible function templates or class template specialization.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
So the simple answer is YES.
This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.
Following may help you:
// Helper macro to create traits to check if function exist.
// Note: template funcName should exist, see below for a work around.
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype) \
template<typename U> \
class traitsName \
{ \
typedef std::uint8_t yes; \
typedef std::uint16_t no; \
template <typename T, T> struct type_check; \
template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*); \
template <typename > static no &chk(...); \
public: \
static bool const value = sizeof(chk<U>(0)) == sizeof(yes); \
}
So with provided namespace with bar
and without bar2
// namespace to test
namespace foo {
template<typename T, typename U> T bar(U);
// bar2 not present
}
Code which check the presence of bar<int, int>
and bar2<int, int>
.
// dummy class which should be never used
namespace detail {
struct dummy;
}
// Trick, so the names exist.
// we use a specialization which should never happen
namespace foo {
template <typename T, typename U>
std::enable_if<std::is_same<detail::dummy, T>::value, T> bar(U);
template <typename T, typename U>
std::enable_if<std::is_same<detail::dummy, T>::value, T> bar2(U);
}
#define COMMA_ , // trick to be able to use ',' in macro
// Create the traits
HAS_TEMPLATED_FUNC(has_foo_bar, foo::bar<T COMMA_ int>, int(*)(int));
HAS_TEMPLATED_FUNC(has_foo_bar2, foo::bar2<T COMMA_ int>, int(*)(int));
// test them
static_assert(has_foo_bar<int>::value, "something went wrong");
static_assert(!has_foo_bar2<int>::value, "something went wrong");
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