Metafunction accepts a type to be checked in the template. If the parenthesis operator is defined in Method <given_type>, then the metafunction should return true. Otherwise false.

#include <iostream> #include <functional> template<typename T> struct Method {}; template<> struct Method<int> { int operator()() { return 1; } }; template<class T> struct is_brackets_op_defined { static void Check(...); template<typename C> static decltype(Method<C>::operator()) Check(const C&); using type = decltype(Check( std::declval< Method<T> >() )); constexpr static bool value = !std::is_same<void, type>(); }; int main() { std::cout << is_brackets_op_defined<float>::value << std::endl; } 

My implementation always returns false, even if a specialization exists (here for an int)

 std::cout << is_brackets_op_defined<float>::value // false std::cout << is_brackets_op_defined<int >::value // false 
  • I don’t know much about the standard, but it’s quite possible that the first function is “stronger” than the template function. The compiler is always lazy. Selects those functions where it is necessary to deduce the types least of all, so most likely only 1 function is constantly used. Try to test. - MrBin
  • Section viable functions - MrBin
  • Not sure why this option doesn't work, but as a simpler alternative you can try std::experimental::is_detected . - HolyBlackCat
  • I wrote a similar metafunction for a slightly different situation (the method was checked in the type), everything worked. I read somewhere that functions with a variable number of arguments are less priority - Jekyll Hyde
  • 2
    @ixSci: Comment out the void version and, as the compiler says, it becomes immediately clear what the error is. The author checks for the presence of operator () in Method<Method<T>> . In the checker, he managed to “wrap” everything twice in the Method pattern. This is how it works: coliru.stacked-crooked.com/a/3058a2ba45ef597a - AnT

3 answers 3

Debugging metaprograms is not the easiest thing to know, but we still try.

To begin, remove the use of is_brackets_op_defined<float> and the declaration static void Check(...); . This will help us to see what the compiler did not like the overloaded version of Check ( code ).

main.cpp:19:30: error: no matching function for call to 'is_brackets_op_defined<int>::Check(Method<int>)' main.cpp:17:42: note: candidate: 'template<class C> static decltype (Method<C>::operator()) is_brackets_op_defined<T>::Check(const C&) [with C = C; T = int]' main.cpp:17:42: note: template argument deduction/substitution failed: main.cpp: In substitution of 'template<class C> static decltype (Method<T>::operator()) is_brackets_op_defined<int>::Check<C>(const C&) [with C = Method<int>]':

Well, as a C , the Method<int> comes to the Check function. Obviously, in this context, the Method<C>::operator() construct gives a compilation error for both int and float . And according to SFINAE, the compiler drops this overload.

We need to somehow change this enemy so that it unfolds into some type for int and gives substitution failed for float . In my opinion &C::operator() will be just right ( code ).


In principle, this can be stopped, but I cannot but suggest a bit to simplify the code. As return values, you can use std::true_type and std::false_type This allows you to throw out the use of std::is_same .


UPD. The question in the commentary suggested a second improvement. Instead of const C& you can use const C* . This will get rid of std::declval , which, for some reason, creates a compile error with incomplete type under GCC.

 #include <iostream> #include <functional> template<typename T> struct Method; template<> struct Method<int> { int operator()() { return 1; } }; template<class T> struct is_brackets_op_defined { static std::false_type Check(...); template<typename C, class = decltype(&C::operator())> static std::true_type Check(const C*); using type = decltype(Check(static_cast<Method<T>*>(nullptr))); constexpr static bool value = type(); }; int main() { std::cout << std::boolalpha << is_brackets_op_defined<float>::value << std::endl; std::cout << std::boolalpha << is_brackets_op_defined<int>::value << std::endl; } 
  • Thank you very much. Now I will know how to look for an error next time. Your answer finally clarified what I was wrong about - Jekyll Hyde
  • @JekyllHyde, then accept this answer, and remove my daw. This answer is better. - ixSci
  • @ixSci, for me it is ambiguous whose answer is better, since your answer gives the option of the right decision - AR Hovsepyan
  • @ARHovsepyan, my only partially answers the question "why does not work?" And offers an alternative solution. This answer is more substantive and helps the author to better understand the error. - ixSci
  • one
    There is no difference between the pointer and the link, in part incomplete type - ixSci

The answer is why your code does not work, I do not have, but there is something that will make it work:

 template<typename C> static decltype(std::declval<C>().operator()()) Check(const C&); 

There is an obvious problem in your code: Method<C>::operator() not a valid C ++, since this is a member function with which nothing is done. And you need to either call her or take her address.

  • Thanks so much for clarifying with: - Jekyll Hyde
  • @JekyllHyde, by the way, I have an article on this topic. You can read, there is a generalized example. - ixSci
  • "There is an obvious problem in your code: Method <C> :: operator () is not a valid C ++ ...". You are right, and you pointed out an error, because of what the code does not work. - AR Hovsepyan
  • one
    @ARHovsepyan, I am grateful to all respondents and find their answers helpful. In the case of the answer above, I liked the fact that the author additionally indicated how to debug - Jekyll Hyde
  • @ixSci, can you tell me what to do if the body of the base structure is not described? Those. template<typename T> struct Method; - Jekyll Hyde

It can be like this. Note that the helper function is also a pattern:

 #include <iostream> template<class T> struct is_brackets_op_defined { private: template<typename C> static char ( & Check(...) )[1]; private: template<typename C> static char ( & Check(decltype(&C::operator ())) )[2]; public: static constexpr bool value{2 == sizeof(Check<T>(nullptr))}; }; struct ClassWith { void operator ()() {} }; struct ClassWithout { }; int main() { std::cout << is_brackets_op_defined<float>::value << std::endl; std::cout << is_brackets_op_defined<ClassWith>::value << std::endl; std::cout << is_brackets_op_defined<ClassWithout>::value << std::endl; return 0; } 

online compiler

  • It turns out that you changed only the return type? - Jekyll Hyde
  • Yes, I needed to find out not that there is a bracket operator in the type, but that the structure with this type of template has this operator. I have already been answered above, but thank you for answering with: - Jekyll Hyde
  • @JekyllHyde No, why. The proposed variant is more generalized and can check the presence of an operator for any type, and not only for the specialization of a particular template. - VTT