2012-04-04 2 views
9

Also, ich habe versucht, eine Funktion wie folgt zu schreiben:Kann typeof (std :: endl) nicht als Template-Parameter haben?

void append_to_stream(std::ostream &stream) 
{ } 

template <typename T, typename... Args> 
void append_to_stream(std::ostream &stream, T first, Args&&... rest) 
{ 
    stream << first; 
    append_to_stream(stream, rest...); 
} 

und nennen es mag:

append_to_stream(stream, 
       std::endl, 
       std::endl); 

Aber das funktioniert nicht. Ich bekomme einen Fehler, der 'zu viele Argumente' für die Funktion sagt. Ich habe es bis zu dem Punkt eingegrenzt, an dem ich weiß, dass die std::endl Schuld ist - wahrscheinlich, weil es eine Funktion ist. Ich habe es geschafft, dies zu "lösen", indem ich eine Struktur mit dem Namen endl deklariere und die <<operator dafür definiere, so dass sie einfach std::endl aufruft. Das funktioniert, fühlt sich aber nicht besonders gut an. Ist es nicht möglich, std :: endl als Template-Argument zu akzeptieren? Die Funktion funktioniert für andere Typen.

Edit: hier ist der Fehler:

src/log/sinks/file_sink.cpp:62:21: error: too many arguments to function ‘void log::sinks::append_to_stream(std::string&, Args&& ...) [with Args = {}, std::string = std::basic_string<char>]’ 

aktualisieren

Der Versuch, den Compiler zu erhalten, die richtigen Vorlage Argumente abzuleiten @MooingDuck schlug vor, dass eine Funktion der folgenden Form verwendet werden könnten:

template<class e, class t, class a> 
    basic_ostream<e,t>&(*)(basic_ostream<e,t>&os) get_endl(basic_string<e,t,a>& s) 
    { 
return std::endl<e,t>; 
    } 

Dies kompiliert jedoch nicht.

Fehler:

src/log/sinks/file_sink.cpp:42:28: error: expected unqualified-id before ‘)’ token 
src/log/sinks/file_sink.cpp:42:53: error: expected initializer before ‘get_endl’ 

Irgendwelche Ideen, warum? Um dies zu kompilieren, habe ich hinzugefügt using namespace std;

+0

sein, was Compiler sind Sie mit?IIRC einige Compiler hatten Probleme mit Rekursion für eine Weile. – Flexo

+0

@awoodland Ich benutze GCC 4.6.2 :) – Max

+2

'std :: endl' ist keine Funktion, es ist eine Vorlage. –

Antwort

14

std::endl ist eine Vorlage, keine Funktion, und der Compiler kann nicht lösen, welche endl zu verwenden.

Versuchen:

append_to_stream(std::cout, 
      std::endl<char, std::char_traits<char>>, 
      std::endl<char, std::char_traits<char>>); 

Oder MooingDuck-Lösung (korrigiert):

template<class e, class t, class a> //string version 
std::basic_ostream<e, t>& (*get_endl(const std::basic_string<e, t, a>&)) 
    (std::basic_ostream<e, t>&) 
{ return std::endl<e,t>; } 

template<class e, class t> //stream version 
std::basic_ostream<e, t>& (*get_endl(const std::basic_ostream<e, t>&)) 
    (std::basic_ostream<e, t>&) 
{ return std::endl<e,t>; } 

int main() { 
    std::ostream& stream = std::cout; 
    append_to_stream(stream, 
       get_endl(stream), 
       get_endl(stream)); 
} 

Hier ist get_endl Lösung, vereinfacht durch C++ 11 decltype Feature:

template<class e, class t, class a> //string version 
auto get_endl(const std::basic_string<e, t, a>&) 
    -> decltype(&std::endl<e,t>) 
{ return std::endl<e,t>; } 

template<class e, class t> //stream version 
auto get_endl(const std::basic_ostream<e,t>&) 
    -> decltype(&std::endl<e,t>) 
{ return std::endl<e,t>; } 

int main() { 
    std::ostream& stream = std::cout; 
    append_to_stream(stream, 
       get_endl(stream), 
       get_endl(stream)); 
} 
+0

Ja, das funktioniert perfekt, danke. Es ist nicht möglich, Template-Funktionen einzugeben, oder? :/ – Max

+0

@Max: Sie können auch eine Funktion in Betracht ziehen. Ich denke so etwas wie "Vorlage basic_ostream & (*) (basic_ostream & os) get_endl (basic_ostream & s) {zurück std :: endl ;}" sollte es tun. –

+0

@MooingDuck Das ist eine gute Idee, ich nehme an, Sie verwenden den Stream, um die Template-Argumente für std :: endl abzuleiten? Das Problem ist, dass die Funktion, die ich verwenden möchte, append_to_file (std :: string, ...) ist, was bedeutet, dass ich am Ort der Instanziierung das Stream-Objekt nicht zur Hand habe: /. Bitte sag mir, ob ich dich falsch verstanden habe! – Max

4

Much einfacher als das Angeben von Vorlagenargumenten oder das Definieren einer ganz neuen Vorlage (!) löst die Überladung auf durch Besetzung.

Dies funktioniert für alle Manipulatoren, während einige Manipulatoren unterschiedlich templatiert werden könnten, zum Beispiel wenn sie vom Benutzer bereitgestellt werden.

Auch sollten Sie T first entweder durch perfekte Weiterleitung oder Const-Verweis übergeben. Es macht nicht viel Sinn, zuerst weiterzuleiten und dann nach Wert zu gehen. Auch ohne einen Aufruf std::forward, rvalue Argumente werden von Wert übergeben werden ... nur nach dem Idiom, wäre es

template <typename T, typename... Args> 
void append_to_stream(std::ostream &stream, T &&first, Args&&... rest) 
{ 
    stream << std::forward<T>(first); 
    append_to_stream(stream, std::forward<Args>(rest) ...); 
} 

http://ideone.com/cw6Mc

+0

Es scheint, dass std :: forward (Rest ...) nicht kompiliert. GCC sagt: 'kein übereinstimmender Aufruf an std :: forward()'. Ich interpretiere das so, dass der Base-Case nicht funktioniert, wo Rest ... leer ist. Dies wird durch die Tatsache unterstützt, dass, wenn ich den Basisfall ändere, um den letzten Parameter zu akzeptieren, es gut kompiliert. Das heißt, ist es vorzuziehen, mit rvalue und std :: forward oder ordentlichen const ref? Was ist der Unterschied? :) – Max

+0

@Max Huch, ich habe das Speditions-Idiom falsch! Beide Packs sollten um einen einzigen "...', aber ich kann nicht ohne weiteres sagen, warum dieser Weg nicht so gut funktioniert. Siehe Aktualisierung. Der Unterschied ist, dass "const &" angibt, dass Sie das Argument nicht ändern werden, daher ist es in diesem Fall wahrscheinlich besser. Beachten Sie, dass 'forward' nicht von rvalue-Verweisen abhängt und den normalen modifizierbaren lvalue-ref passiert, wenn Sie ihm einen einfachen lokalen Variablennamen geben. – Potatoswatter