5

So with SFINAE and c++11, it is possible to implement two different template functions based on whether one of the template parameters can be substituted.

For example

struct Boo{
   void saySomething(){ cout << "Boo!" << endl; }
};

template<class X>
void makeitdosomething(decltype(&X::saySomething), X x){
   x.saySomething();
}

template<class X>
void makeitsaysomething(int whatever, X x){
    cout << "It can't say anything!" << endl;
}


int main(){
   makeitsaysomething(3);
   makeitsaysomething(Boo());
}

or something along that line.

My question is.. how does one do the same thing, but for non-member functions?

In particular I'm trying to check if there's such thing as an:

operator<<(std::ostream& os, X& whateverclass);

that exists. Is it possible to test it?

edit: the question is different to : Is it possible to write a template to check for a function's existence? in that I'm trying to see whether a function exists, not a method

Community
  • 1
  • 1
kamziro
  • 7,882
  • 9
  • 55
  • 78
  • possible duplicate of [Is it possible to write a C++ template to check for a function's existence?](http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence) – edmz Mar 28 '15 at 15:39

3 Answers3

8

I find the void_t trick to be usually preferable to the traditional SFINAE method shown in the other answer.

template<class...> using void_t = void; // now in the C++17 working paper!
// GCC <= 4.9 workaround:
// template<class...> struct voider { using type = void; };
// template<class... T> using void_t = typename voider<T...>::type;

template<class, class = void>
struct is_ostreamable : std::false_type {};

template<class T>
struct is_ostreamable<T, void_t<decltype(std::declval<std::ostream&>() <<
                                         std::declval<T>())>> : std::true_type {};

The partial specialization is selected if and only if the expression is well-formed.

Demo. Note that the & in std::declval<std::ostream&>() is important, because otherwise std::declval<std::ostream>() is an rvalue and you'll get ostream's catchall rvalue stream insertion operator, and report that everything is streamable.

The above code checks for an operator<< that can accept a T rvalue . If you want to check for one that accepts an lvalue T, then use std::declval<T&>().

T.C.
  • 133,968
  • 17
  • 288
  • 421
4

Using SFINAE, it's possible like this:

template <typename T>
class has_ostream_lshift
{
  struct no {};

  template <typename T2>
  static decltype(std::declval<std::ostream&>() << std::declval<T2>()) test(int);

  template <typename T2>
  static no test(...);

public:
  enum { value = ! std::is_same<no, decltype(test<T>(0))>::value};
};
erenon
  • 18,838
  • 2
  • 61
  • 93
0

There is no need to use void_t. With decltype and comma separator, it's possible like this:

template <class, class = void> struct is_ostreamable : std::false_type {};

template <class T>
struct is_ostreamable<
    T, decltype(std::declval<std::ostream &>() << std::declval<T>(), void())>
    : std::true_type {};

inhzus
  • 3
  • 2