SFINAE: detecting the existence of a template function requiring explicit specialization

As a continuation of my previous question , I am trying to discover the existence of a template function that requires explicit specialization.

My current working code detects functions without a pattern (thanks to the help of DyP) if they take at least one parameter so that you can search for a dependent search by name:

// 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 macro ENABLE_FOO_BAR is only for testing, in my real code I do not have such a macro, otherwise I would not use SFINAE)

This also works great with template functions when their template arguments can be automatically output by the compiler:

 namespace foo { #if ENABLE_FOO_BAR template<typename T> int bar(T); #endif } 

However, when I try to find a template function that requires explicit specialization, static_assert kicks 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 cannot output the arguments to the bar() template, so detection is not performed. I tried to fix this by explicitly highlighting 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 when foo::bar() does not 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 to explicitly specialize failed because the compiler does not know that bar is a template.

I will spare you everything that I tried to fix, and let's get straight to the point: how can I detect the existence of such a function as template<typename T, typename U> T bar(U); that requires explicit specialization to instantiate?

+8
c ++ c ++ 11 templates sfinae
source share
1 answer

The 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 the provided namespace with bar and without bar2

 // namespace to test namespace foo { template<typename T, typename U> T bar(U); // bar2 not present } 

Code that checks for 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"); 
+2
source share

All Articles