позвоните моему оператору шаблона << только если не найдено другого - PullRequest
1 голос
/ 08 февраля 2012

Я хочу напечатать состояние моих объектов через функцию-член toString, но я хочу вызвать его через String::toString(var). Но я хочу сделать это только для объекта, для которого не определено operator<<.

Моя попытка ниже. У меня есть шаблон operator<<, который вызывает toString, но я надеялся, что этот оператор будет учитываться только в том случае, если не найден другой подходящий operator<<. Я был явно неправ:)

#include <iostream>
#include <sstream>

struct WithToString
{
    std::string toString()
    { return std::string ("foo") ; }
};

struct WithoutToString {};

struct String
{
    template <typename T>
    static std::string toString(T & var)
    {
        std::stringstream s;
        s << var;
        return s.str();
    }
};

template <typename T>
std::ostream & operator<<(std::ostream & s, T & var)
{
    s << var.toString();
    return s;
}

int main(int argc, char *argv[])
{
    int i = 5;
    std::cout << String::toString(i); //fine, prints 5

    WithToString w;
    std::cout << String::toString(w); //fine, prints foo

//    WithoutToString ws;
//    std::cout << String::toString(ws); //fine - give "toString is not a member" error

//    const char * s = "bar";
//    std::cout << String::toString(s); //error - operator << is ambiguous

//    std::string s = "bar";
//    std::cout << String::toString(s); //error - toString is not a member

    return 0;
}

Как добиться этого поведения?

EDIT

вот моя другая попытка, но опять-таки не получается с помощью строки и символа *

template <class Type, class V>
class HasOperatorShiftLeft
{
    template <typename T, T> struct TypeCheck;

    typedef char Yes;
    typedef long No;

    template <typename T> struct ToString
    {
        typedef std::ostream & (T::*fptr)(V);
    };

    template <typename T> static Yes HasOpShift(TypeCheck< typename ToString<T>::fptr, &T::operator<< >*);
    template <typename T> static No  HasOpShift(...);

public:
    static bool const value = (sizeof(HasOpShift<Type>(0)) == sizeof(Yes));
};

template <typename T, int A>
struct toStringStr{};

template <typename T>
struct toStringStr<T,1>
{
    static std::string toString(T & var)
    {
        std::stringstream s;
        s << var;
        return s.str();
    }
};

template <typename T>
struct toStringStr<T,0>
{
    static std::string toString(T & var)
    {
        return var.toString();
    }
};

template <typename T>
std::string toString(T & var)
{
    return toStringStr<T,HasOperatorShiftLeft<std::ostream,T>::value>::toString(var);
}

EDIT моя последняя попытка опубликована как Ответ, потому что я думаю, что она работает

Ответы [ 2 ]

3 голосов
/ 08 февраля 2012

Это на самом деле довольно легко, хотя и глупо, как описано в комментариях.В C ++ 11:

template<class T>
auto operator<<(std::ostream& os, T const& val)
    -> decltype(os << val.toString())
{
  return os << val.toString();
}

Эта функция будет существовать только в том случае, если внутри decltype(..) находится допустимое выражение.Теперь просто перетащите все в std::ostream& и назовите это день.Если у типа есть и toString, и перегруженный operator<< для std::ostream&, хорошо, жестко.Вы получите ошибку «неоднозначный вызов перегруженного operator<<».


Для C ++ 03 есть еще один вариант.Поскольку вы, кажется, вроде не любите свободные функции, я предполагаю, что вам нравятся интерфейсы.Таким образом, получите себе базовый класс Streamable с методом virtual std::string toString() const = 0 и перегрузку operator<< только для этого.Presto, у вас есть operator<< для всех классов, которые реализуют этот интерфейс!

struct Streamable{
  virtual std::string toString() const = 0;
};

std::ostream& operator<<(std::ostream& os, Streamable const& s){
  return os << s.toString();
}

Или вы можете даже перейти на мета-уровень, чтобы избавиться от бесполезного вызова виртуальной функции:

template<class D>
struct Streamable{
  std::string toString() const{
    return static_cast<D const&>(*this).toString();
  }
};

template<class D>
std::ostream& operator<<(std::ostream& os, Streamable<D> const& s){
  return os << s.toString();
}

// example:
struct Blub
  : public Streamable<Blub>
{
  // implement toString() ...
};
0 голосов
/ 09 февраля 2012

А как насчет этого? Я думаю, что это работает просто отлично ...

struct String
{
    template <typename T>
    static std::string toString(const T & var)
    {
        std::stringstream s;
        s << var;
        return s.str();
    }
};

template<typename Elem, typename Traits, typename T>
std::basic_ostream<Elem, Traits> & operator <<(std::basic_ostream<Elem, Traits> & s, const T & var)
{
    s << var.toString();
    return s;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...