
On Jun 12, 2015, at 09:30, Peter Dimov <lists@pdimov.com> wrote:
But the compiler is able to instantiate those eagerly if a type argument is specified (char) that does not depend on the outer template parameter.
It is. What I meant was: <snip>
template <class Ch, class Tr, class Enum> inline std::basic_ostream<Ch, Tr>& operator <<(std::basic_ostream<Ch, Tr>& os, const Enum& value) { return os << value.template _to_basic_string<Ch>(); }
You'll have to define _to_basic_string for char, wchar_t, char16_t and char32_t if you want to cover all cases, of course.
If you want to restrict yourself to char-based streams:
template <class Tr, class Enum> inline std::basic_ostream<char, Tr>& operator <<(std::basic_ostream<char, Tr>& os, const Enum& value) { return os << value._to_string(); }
Ah, cool, yes. As long as I only actually support char streams, this looks almost like an eta-expansion. I was able to use it on std::string as well (std::basic_string<Char>), and replaced the reference to ios_base::failbit with std::basic_istream<Char, Tr>::failbit, because ios_base has no template parameters. This makes the function bodies much cleaner - thanks! It also reminds me to deal with non-char streams. I still need hide<> (now renamed) on the return types, however, for SFINAE, to prevent ambiguity. Actually, this had made operator << a sort of degenerate case, since both the function header and body depend on the parameter Enum, so the extra parameters Ch and Tr are not necessary until I have _to_basic_string<>. Best, Anton