2016-11-01 2 views
7

Ich möchte einen Operator < < für das Streaming meiner Klasse implementieren (sagen wir es Paragraph). Klasse Paragraph hat einige private Daten, aus diesem Grund möchte ich den (freistehenden) Operator < < Funktion ein Freund sein. Also tue ich wie vorgeschlagen, z. B. here on SO. friend Anweisung, implementieren Sie die operator<< und alles ist gut.Muss ich mich wirklich für einen Freund-Operator << für eine Klasse in einem Namespace bücken?

Aber jetzt möchte ich Paragraph in einen Namensraum, sagen namespace foo. Es funktioniert nicht mehr! Wenn ich schreibe:

namespace foo { 
class Paragraph { 
    public: 
     explicit Paragraph(std::string const& init) :m_para(init) {} 
     std::string const& to_str() const { return m_para; } 
    private: 
     friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
     std::string  m_para; 
}; 
} // namespace foo 

Der Compiler sagt mir, ich habe foo::operator<< befreundeten, nicht ::operator<<. OK Fair genug. Also ersetze ich die Freundin Linie mit:

friend std::ostream & ::operator<<(std::ostream &os, const Paragraph& p); 

aber ich erhalte eine Fehlermeldung erneut (von GCC 5.4.0), mir zu sagen, die ::operator<< hat nicht erklärt worden ist. Ok, lass es uns dann erklären. Wird diese Arbeit ?:

namespace foo { 
std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); 
class Paragraph { 
    public: 
     explicit Paragraph(std::string const& init) :m_para(init) {} 
     std::string const& to_str() const { return m_para; } 
    private: 
     friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
     std::string  m_para; 
}; 
} // namespace foo 

Nein, es weiß nicht, über Absatz, wenn die Deklaration von ::operator< lesen. Ok, lass uns vorwärts deklarieren:

namespace foo { 
class Paragraph; 
std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); 
class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

kein Glück. Ich erhalte die seltsamen Fehler:

f.cpp:23:16: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) 
       ^
f.cpp:11:15: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) 
std::ostream& ::operator<<(std::ostream &os, const foo::Paragraph& p); 

... und hier war ich der globale Namensraum zu denken und die :: Namespace sind die gleiche Sache ... Arent‘sie? (Seufzer). Egal, lassen Sie uns die Erklärung bewegen aus dem Namensraum:

class foo::Paragraph; 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); 
namespace foo { class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

noch nicht gut genug, jetzt bekomme ich die Fehlermeldung „‚foo‘wurde nicht deklariert“. (Zähneknirschen) gut! Sei auch so!

namespace foo { class Paragraph; } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); 

namespace foo { class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

so das, aber nicht weniger als das, funktioniert. Das ist schrecklich! Sicherlich muss es eine Art weniger wortreichen Weg geben, es zu tun ... richtig?

Hinweis: Angenommen, die operator<< kann nicht inline sein und muss separat definiert werden.

Antwort

6

Es scheint, Ihr Problem ergibt sich aus nicht erkennen, wie ADLoperator<< das Recht finden können, solange sie die gleiche ist Namensraum wie Paragraph. So erweitern Sie Ihr erstes Beispiel

// this is what you have already 
namespace foo { 
class Paragraph { 
public: 
    explicit Paragraph(std::string const& init) :m_para(init) {} 
    std::string const& to_str() const { return m_para; } 
private: 
    friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
    std::string  m_para; 
}; 
} // namespace foo 

// Now we can add a definition here, or in a different TU 
namespace foo { 
std::ostream& operator<<(std::ostream& os, const Paragraph& p) { 
    return os << p.m_para; 
} 
} // namespace foo 


int main() { 
    foo::Paragraph p("hello"); 
    // finds your operator<< using adl 
    std::cout << p << '\n'; 
} 
+1

Ihr erster Satz traf den Nagel auf den Kopf. Oder vielleicht sollte ich meinen Kopf sagen. – einpoklum

+0

@einpoklum Darüber hinaus ist dies die einzige Lösung, die garantiert, dass die Namenssuche erfolgreich ist. ':: operator <<' würde in einigen Kontexten nicht gefunden werden, da es nicht Teil des ADL-Lookup-Sets ist. –

3

Setzen Sie einfach die Definition des Operators << in den Namensraum zusammen mit Paragraph. Argument-abhängige Suche findet es, wenn Sie ein Paragraph-Objekt in einen Stream einfügen.

// myheader.h: 
namespace ns { 
    struct x { 
     /* ... */ 
     friend std::ostream& operator<<(std::ostream&, const x&); 
    }; 
} 

// myheader.cpp: 
#include "myheader.h" 
namespace ns { 
    std::ostream& operator<<(std::ostream& os, const x& xx) { 
     os << xx.whatever() << '\n'; 
     return os; 
    } 
} 

Oder, wenn es klein genug ist inlining zu rechtfertigen, genau das tun:

// myheader.h: 
namespace ns { 
    struct x { 
     /* ... */ 
     friend std::ostream& operator<<(std::ostream&, const x&); 
    }; 
    inline std::ostream& operator<<(std::ostream& os, const x& xx) { 
     os << xx.whatever() << '\n'; 
     return os; 
    } 
} 
Verwandte Themen