I'm a beginner C++ developer and I have a question about toString
and ostream
operator integration via templates.
I have such code:
struct MethodCheckerTypes{
typedef unsigned char TrueType;
typedef long FalseType;
};
template<typename T>struct HasToString{
template<typename K>static typename MethodCheckerTypes::TrueType test(decltype(&K::toString));
template<typename> static typename MethodCheckerTypes::FalseType test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(typename MethodCheckerTypes::TrueType);
typedef decltype(test<T>(0)) ValueType;
};
template<typename T, typename K> struct IfDef{};
template<> struct IfDef<typename MethodCheckerTypes::TrueType, ostream&>{
typedef ostream& type;
};
class WithToString{
public:
string toString() const{
return "hello";
}
};
template<typename F, typename CharT, typename Traits> typename IfDef<typename HasToString<F>::ValueType, basic_ostream<CharT, Traits>&>::type operator<<(basic_ostream<CharT, Traits>& out, const F &ref){
out<<ref.toString().c_str();
return out;
}
int main(int argc, char **argv){
WithToString hasToString;
cout<<hasToString<<endl;
return 0;
}
The code has been compilled without errors, and the application executed successfuly. Is it good to use such an approach? I wanted to implement it without any help from boost.
The approach to implementing operator<<
by itself is normal. But using parts of the language you do not understand is bad practice (I do not believe that a beginner can write such code). You have two alternatives: to implement a toString
member function or overload operator<<(std::ostream&, T)
. The latter approach enables you to use boost::lexical_cast
to convert an object to a string. As for me, the latter approach is more C++ish, if you can do something without a member function it is better to do so.