调用我的模板运算符<<只有没有其他人找到

时间:2012-02-08 17:31:43

标签: c++ templates

我想通过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;
}

如何实现这种行为?

修改

这是我的另一次尝试,但又失败了,字符串和char *

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);
}

修改 我的最新尝试发布为答案,因为我认为,它有效

2 个答案:

答案 0 :(得分:3)

这实际上非常简单,尽管如评论中所描述的那样愚蠢。使用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() ...
};

答案 1 :(得分:0)

这个怎么样? 我觉得它运作得很好......

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;
}