Search code examples
c++boostoperator-overloadingtype-traits

How to check if operator<< is defined for a class?


I want to use type traits to check if operator << is defined for some classes.

So I've defined 2 classes A and B which are similar but the operator << is overloaded only for A.

To do that I am making use of boost::is_detected.

The running code is the following:

#include<iostream>
#include <boost/type_traits/is_detected.hpp>
class A
{
  public:
    std::string name;
    int64_t age;
    bool b;

};

class B
{
  public:
    std::string name;
    int64_t age;
    bool b;

};

std::ostream& operator<<(std::ostream& destination, const A& type)
{
    return destination << "name=" << type.name << ", age=" << int(type.age)<<std::endl;
}


template <typename T>
using has_operator_insertion_check = decltype(void(&std::decay_t<T>::operator<<));

template <typename T>
using has_operator_insertion = boost::is_detected<has_operator_insertion_check, T>;

template <typename T>
constexpr bool has_operator_insertion_v = has_operator_insertion<T>::value;


int main()
{
  A a, b;
  a.name = "Ciaooooo";
  a.age = 65536;
  a.b = true;
  std::cout<<"A has operator '<<'? "<<has_operator_insertion_v<A><<std::endl;
  std::cout<<"B has operator '<<'? "<<has_operator_insertion_v<B><<std::endl;

  return 0;
}

However, this is not working and the output I am getting is:

A has operator '<<'? 0
B has operator '<<'? 0

Why am I getting identical results?


Solution

  • std::ostream& operator<<(std::ostream& destination, const A& type)
    

    is a free function, not a member function and

    decltype(void(&std::decay_t<T>::operator<<));
    

    only checks for the presence of a member function.

    You can rewrite your type trait to work with member and non-member output operators like

    // false case
    template <typename T, typename = void>
    struct has_operator_insertion_check : std::false_type {};
    
    // partial specialization, 
    // if std::declval<std::ostream&>() << std::declval<T>() is valid then true
    template <typename T>
    struct has_operator_insertion_check<T, std::void_t<decltype((std::declval<std::ostream&>() << std::declval<T>()))>> : std::true_type {};
    
    template <typename T>
    constexpr bool has_operator_insertion_v = has_operator_insertion_check<T>::value;
    

    You can see this working in this live example.