Voglio avere diverse funzioni globali to_string()
sovraccaricate che prendono un tipo T
e lo convertono nella sua rappresentazione di stringa. Nel caso generale, voglio essere in grado di scrivere:
template inline typename enable_if<!std::is_pointer::value && has_insertion_operator::value, void>::type to_string( T const &t, OutputStringType *out ) { std::ostringstream o; o << t; *out = o.str(); }
La mia implementazione di has_insertion_operator
finora è:
struct sfinae_base { typedef char yes[1]; typedef char no[2]; }; template struct has_insertion_operator : sfinae_base { template static yes& test( U& ); template static no& test(...); static std::ostream &s; static T const &t; static bool const value = sizeof( test( s << t ) ) == sizeof( yes ); // line 48 };
(Prende in prestito da questo e da questo .) Sembra funzionare. Ma ora voglio avere una versione sovraccaricata di to_string
per i tipi che non hanno operator<<
ma hanno la loro funzione membro to_string()
, cioè:
template inline typename enable_if<!has_insertion_operator::value && has_to_string::value, void>::type to_string( T const &t, OutputStringType *out ) { *out = t.to_string(); }
L’implementazione di has_to_string
è:
#define DECL_HAS_MEM_FN(FN_NAME) \ template \ struct has_##FN_NAME : sfinae_base { \ template struct type_check; \ template static yes& test(type_check*); \ template static no& test(...); \ static bool const value = sizeof( test(0) ) == sizeof( yes ); \ } DECL_HAS_MEM_FN( to_string );
(Questa parte sembra funzionare bene. È adattata da questo .) Tuttavia, quando ho:
struct S { string to_string() const { return "42"; } }; int main() { string buf; S s; to_string( s, &buf ); // line 104 }
Ottengo:
foo.cpp: In instantiation of 'const bool has_insertion_operator::value': foo.cpp:104: instantiated from here foo.cpp:48: error: no match for 'operator<<' in 'has_insertion_operator::s << has_insertion_operator::t'
Sembra che la SFINAE non stia succedendo. Come scrivo has_insertion_operator
correttamente in modo tale da determinare se un operator<<
globale operator<<
è disponibile?
A proposito: sto usando g ++ 4.2.1 (quello che viene fornito come parte di Xcode su Mac OS X). Inoltre, mi piacerebbe che il codice fosse solo C ++ 03 standard senza librerie di terze parti, ad esempio, Boost.
Grazie!
Sarei dovuto essere semplicemente più fedele a questa risposta. Un’implementazione funzionante è:
namespace has_insertion_operator_impl { typedef char no; typedef char yes[2]; struct any_t { template any_t( T const& ); }; no operator<<( std::ostream const&, any_t const& ); yes& test( std::ostream& ); no test( no ); template struct has_insertion_operator { static std::ostream &s; static T const &t; static bool const value = sizeof( test(s << t) ) == sizeof( yes ); }; } template struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator { };
Credo che in realtà non faccia affidamento su SFINAE.
L’inizializzatore di value
sulla riga 48 non è in un contesto in cui funziona SFINAE. Prova a spostare l’espressione nella dichiarazione della funzione.
#include struct sfinae_base { typedef char yes[1]; typedef char no[2]; }; template struct has_insertion_operator : sfinae_base { // this may quietly fail: template static yes& test( size_t (*n)[ sizeof( std::cout << * static_cast(0) ) ] ); // "..." provides fallback in case above fails template static no& test(...); static bool const value = sizeof( test( NULL ) ) == sizeof( yes ); };
Tuttavia, devo mettere in discussione la quantità di sofisticazione che sta accadendo in questo. Vedo meccanismi non ortogonali che si to_string
uno contro l’altro ( to_string
vs. operator<<
) e sento che le ipotesi sbagliate vengono sballottate (ad esempio, l' operator<<
è globale rispetto a un membro, anche se il codice implementato sembra OK in quel considerare).