2

I have following code:

template <typename T>
struct function_traits
{
    typedef decltype(&T::operator()) test_type;
    typedef std::true_type res_type;
};

template <typename T>
struct function_traits
{
    typedef std::false_type res_type;
};

In other words, I want to know whether type has operator (). I thought that I can use SFINAE way to do this. However compiler tells:

'function_traits' : class template has already defined.

What's wrong with such code?

P.S.: here is simple usage:

auto f1 = [](const int a){ std::cout << a << std::endl; };
function_traits<decltype(f1)>::res_type;
auto f2 = false;
function_traits<decltype(f2)>::res_type;

EDIT: I am using c++ 11 standard

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
Alex Aparin
  • 4,393
  • 5
  • 25
  • 51
  • 3
    If you need this for actual use rather than educational purposes `std::is_callable` should work for you. –  Feb 27 '17 at 08:22
  • @user2176127, oh, thanks, it is interesting. But I am interested in educational purposes. And I am sorry, I want to use c++ 11 standard (I edited question) – Alex Aparin Feb 27 '17 at 08:25
  • 1
    Would this work? (I am not sure) http://stackoverflow.com/questions/257288 – javaLover Feb 27 '17 at 08:29
  • 1
    @javaLover, thanks! it works! – Alex Aparin Feb 27 '17 at 08:36
  • In your question, you really define function_traits twice. I think you need specialization. – Ron Tang Feb 27 '17 at 10:24
  • You may be interested by [std::is_detected](http://stackoverflow.com/documentation/c%2b%2b/1169/sfinae-substitution-failure-is-not-an-error/18585/is-detected). – Jarod42 Feb 27 '17 at 12:53

2 Answers2

6

Encouraged by a positive feedback, I will post a brief answer here.

The problem is that you cannot define a class twice, but you did this two times :-

template <typename T>
struct function_traits {  .... some code ..... }    

C++ doesn't allow that.

To check whether a function is exist, there is already a question about it, you can modify it to support the operator().

Here is a demo, very-slightly modified from Nicola Bonelli's answer there.

#include <iostream>

struct Hello
{
    void operator()() {  }
};

struct Generic {};    

// SFINAE test
template <typename T>
class has_parenthesis
{
    typedef char one;
    typedef long two;

    template <typename C> static one test( decltype(&C::operator()) ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
    std::cout << has_parenthesis<Hello>::value << std::endl;    //print 1
    std::cout << has_parenthesis<Generic>::value << std::endl;  //print 0
    return 0;
}

I just knew a few minutes ago that it also works for operator().

Edit: I changed typeof to decltype as StoryTeller and LmTinyToon recommended. Thank.

Community
  • 1
  • 1
javaLover
  • 6,347
  • 2
  • 22
  • 67
3

Maybe using the void_t is much more simple:

template <typename T,class=void>
struct callable_without_args: std::false_type{};

template <typename T>
struct callable_without_args<T
          ,std::void_t<decltype(std::declval<T>()())>>
  :std::true_type{};

It could be even simpler if your compiler provided concepts:

template<T>
concept bool CallableWithoutArgs= requires(T&& a){
     (T&&)(a)();
};
Oliv
  • 17,610
  • 1
  • 29
  • 72
  • I think "concepts" is so awesome. Is it already used in development of commercial programs? If so, I will study it now. :) – javaLover Feb 27 '17 at 09:24
  • 1
    It has drastically increased my productivity, so I am actually using it for internal tools. It not only changes the way you program, but also the way you design, I was not expecting it when I tried it 6 month ago. Since then, I am always using it. But it is not tomorow that concepts will be used to program safety critical embedded systems!! – Oliv Feb 27 '17 at 09:31