我想通过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);
}
修改 我的最新尝试发布为答案,因为我认为,它有效
答案 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;
}