2012-11-19 19 views
8

ich zur Zeit typisieren bin Lesen „Effective C++“ und gibt es ein Kapitel, das Code similiar dazu enthält:C++ Hinzufügen von Freund zu einer Template-Klasse, um

template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 
}; 

template <typename T> 
Num<T> operator*(const Num<T>& lhs, const Num<T>& rhs) { ... } 

Num<int> n = 5 * Num<int>(10); 

Das Buch sagt, dass dies nicht funktioniert (und in der Tat nicht), weil Sie nicht erwarten können, dass der Compiler implizite Typumwandlung verwendet, um eine Vorlage zu spezialisieren.

Als Lösung wird empfohlen, die "friend" -Syntax zu verwenden, um die Funktion innerhalb der Klasse zu definieren.

//It works 
template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 

    friend 
    Num operator*(const Num& lhs, const Num& rhs) { ... } 
}; 

Num<int> n = 5 * Num<int>(10); 

Und das Buch schlägt vor, dieses Freund-Erklärung, was zu verwenden, wenn ich implizite Konvertierung in einen Template-Klasse Typen benötigen. Und alles scheint einen Sinn zu ergeben.

Aber warum kann ich nicht das gleiche Beispiel mit einer gemeinsamen Funktion arbeiten, nicht ein Operator?

template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 

    friend 
    void doFoo(const Num& lhs) { ... } 
}; 

doFoo(5); 

Diesmal der Compiler Beschwerden, dass er überhaupt keine "DoFoo" finden kann. Und wenn ich die DoFoo außerhalb der Klasse deklariere, bekomme ich die vernünftige nicht übereinstimmende Typen Fehler. Scheint so, als ob der "Freund ..." Teil einfach ignoriert wird.

Also gibt es ein Problem mit meinem Verständnis? Was ist der Unterschied zwischen einer Funktion und einem Operator in diesem Fall?

+0

Beachten Sie, wie mindestens ein Argument von 'Operator *' muss eine 'Num ' sein. Dies ist bei 'doFoo' nicht der Fall. Ihr Typ könnte von "int" konstruierbar sein, ohne dass "T" selbst "int" ist. Der Spaß ist, dass es immer noch nicht funktionieren würde, wenn Ihre konvertierenden Konstruktoren eine Entsprechung zu den Template-Argumenten der Klassenvorlage hätten. Sie können ein Klassenvorlagenargument nicht über eine Elementfunktion dieser Vorlage ableiten. Sie können das umgehen, indem Sie eine 'make_num'-Funktion bereitstellen. – pmr

Antwort

3

Der Grund dafür ist, dass hier

doFoo(5); 

der Compiler keine Möglichkeit hat, foo zu finden, einen int Parameter gegeben. Dies wäre das Äquivalent Ihr Freund Operator wie folgt zu nennen:

Num<int> n = 5 * 10; 

Dies wird „Arbeit“, aber nicht durch die friend operator* in Ihrer Num Klasse definiert nennen, aber die eingebaute in operator* für ganze Zahlen durch den Aufruf und Dann benutze die implizite Konvertierung von Num Converting Konstruktor.

+0

Dies ist nicht wirklich der Grund, Sie können dasselbe Problem reproduzieren, indem Sie die Vorlage und damit das Template-Argument entfernen. Die Antwort ist, wie die Suche nach Freundschaftsdeklarationen sucht (oder nicht findet). –

+0

@ DavidRodríguez-dribeas OK, das macht Sinn. Es ist also ADL, und es schlägt fehl, weil das Argument nicht im Klassennamespace liegt. – juanchopanza

+0

Art von. Es ist ADL, aber es hat nichts mit dem Namensraum zu tun, in dem dies definiert ist. Die Suche findet nur eine Freundschaftsdeklaration über ADL, aber wir sprechen nicht über das Hinzufügen eines Namespace in die Suche, sondern den * -Typ *. Während die meisten Leute ADL als den Namespace der Argumente einstufen, macht es mehr als das: es sucht nach Deklarationen, die nur * innerhalb * der Typen der Argumente verfügbar sind, was hier der Fall ist. –

0

Ja diese Code-Compiler weiß nicht, wie man damit arbeitet. wie doFoo (5)

Compiler weiß nicht 5

+2

Der Compiler ** weiß ** '5' ist ein int. Was es nicht wissen kann ist, dass es eine Art "Num " von diesem int konstruieren soll. – juanchopanza

1

Das Kernproblem ist Lookup-int ist. Eine friend Deklaration stellt eine Deklaration einer Namespace-Level-Funktion bereit, aber die Deklaration ist nur innerhalb der Klasse verfügbar, die mit ihr kommuniziert. Im Beispiel sieht das Buch vor, dass dies kein Problem darstellt: Die Funktion benötigt zwei Argumente vom einschließenden Typ, solange eine davon vom einschließenden Typ ist, wird Argument abhängige Suche innerhalb der Definition der Klasse suchen und den Operator finden. In Ihrem Fall ist das nicht der Fall. Da es ein einzelnes Argument gibt, das eine Konvertierung benötigt, wird der Compiler nicht in die Definition der Klasse schauen.

Hinweis, dass dies unabhängig von Vorlagen und Umbauten:

class A { 
    friend void f(int) {} 
    friend void g(int, A) {} 
}; 
int main() { 
    f(5);   // Error: lookup cannot find 'f' declared *only* inside A 
    g(5,A());  // Ok, one argument is 'A', lookup will find the function 
} 

Im obigen Fall, wo es keine Vorlagen beteiligt ist, könnten Sie möglicherweise eine Erklärung auf Namespace-Ebene hinzufügen, um es zu beheben, aber das ist nicht wirklich eine Option für Vorlagenklassen.

class A { 
    friend void f() { std::cout << "inside A\n"; } 
}; 
void f(int);  // only declaration 
int main() { 
    f(5);   // "inside A" 
} 

Dies kann nicht für eine Vorlage (und für alle Instanziieren Typen) als Freund Erklärung ist eine Erklärung einer Nicht-Template-Funktion erfolgen. Obwohl Sie mit dem Code spielen könnten, nur um die Prüfung:

template <typename T> 
struct Num { 
    Num(int x) ... 
    friend void f(Num const &); 
}; 
Num<int> f(Num<int> const &); // only declaration 
int main() { 
    f(5); 
} 
Verwandte Themen