Overload operator with pattern but not overridden

I want to define specialization operator<<

using a template, but I do not want it to break the behavior of this operator if it is already defined for some data type.

enum State {Open, Locked};
enum Input {Coin, Push};

std::string ToString(State c){
  switch (c) {
    case Locked : return "Locked";
    case Open : return "Open";
  }
}

std::string ToString(Input c){
  switch (c) {
    case Coin : return "Coin";
    case Push : return "Push";
  }
}

template<typename T>   //obviously, a bad idea
std::ostream& operator<<(std::ostream& s, T c) {
  return s<<ToString(c);
}

      

later in the code I would like to use:

int main() {
  std::cout<<Coin<<std::endl;
  std::cout<<Open<<std::endl;
  std::cout<<std::string("normal string")<<std::endl;
}

      

Unsurprisingly, the above is a compilation error:

error: ambiguous overload foroperator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ andstd::string {aka std::basic_string<char>}’)
   std::cout<<std::string("normal string")<<std::endl;

      

(much more)

Q: How do I tell the compiler to ignore the template if the function / operator already has a definition?

+3


source to share


3 answers


You can use SFINAE to instantiate a template only for types supported by overloads ToString()

, for example

template<typename T, typename = decltype(ToString(std::declval<T>()))>
std::ostream& operator<<(std::ostream& s, T c) {
  return s<<ToString(c);
}

      



LIVE

+6


source


To add to @songyuanyao's answer , do two more things:

  • Wrap your code in a namespace.
  • Fully qualify the identifier in decltype

    the namespace. That is, something like decltype(MyNS::ToString(std::declval<T>()))

    .

Your print statements will still run ADL aware, but you won't run search rules if your statement is somehow a candidate with a type that it also defines ToString

in a different namespace. 1




1 If you have templates in your namespace, ADL will also consider their parameter namespaces. Which may put you in the good of a different definition ToString

during an unqualified search.

+2


source


I figured out that I can do this using C ++ concepts

template<typename T>
concept bool HasToString = requires(T a) {
  { ToString(a) } -> std::string;
};

std::ostream& operator<<(std::ostream& s, HasToString c) {
  return s<<ToString(c);
}

      

This requires the gcc.6 flag and -fconcepts

.

0


source







All Articles