仅当找不到其他运算符时才调用我的模板运算符<<

call my template operator<< only if no other found

本文关键字:运算符 lt 我的 找不到 其他 调用      更新时间:2023-10-16

我想通过toString成员函数打印对象的状态,但我想通过String::toString(var)调用它。但我只想对并没有定义operator<<的对象执行此操作。

我的尝试在下面。我有调用toString的模板operator<<,但我希望只有在没有找到其他合适的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;
}

如何实现这种行为?

编辑

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

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

编辑我的最新尝试被发布为Answer,因为我认为它在

中有效

这实际上很容易,尽管按照注释中的描述做起来很愚蠢。使用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,还有另一个选项。由于您似乎有点不喜欢免费函数,我假设您喜欢接口。因此,使用virtual std::string toString() const = 0方法为自己创建一个Streamable基类,并仅为此重载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() ...
};

这个怎么样?我觉得效果很好。。。

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