2016-10-31 2 views
16

Bitte werfen Sie einen Blick auf dieses Code-Snippet. Ich weiß, dass es nicht viel Sinn macht, ist es nur das Problem veranschaulichen soll ich begegnet bin:Die Memberschablonenfunktion der Klasse A, die in Klasse B als Freund deklariert wurde, kann nicht auf private Member der Klasse A zugreifen (nur Clang).

#include <iostream> 
using namespace std; 

struct tBar 
{ 
    template <typename T> 
    void PrintDataAndAddress(const T& thing) 
    { 
     cout << thing.mData; 
     PrintAddress<T>(thing); 
    } 

private: 
    // friend struct tFoo; // fixes the compilation error 

    template <typename T> 
    void PrintAddress(const T& thing) 
    { 
     cout << " - " << &thing << endl; 
    } 
}; 

struct tFoo 
{ 
    friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&); 
    private: 

    int mData = 42; 
}; 

struct tWidget 
{ 
    int mData = 666; 
}; 

int main() 
{ 
    tBar bar; 
    bar.PrintDataAndAddress(tWidget()); // Fine 

    bar.PrintDataAndAddress(tFoo()); // Compilation error 

    return 0; 
} 

Der obige Code löst den folgenden Fehler:

source_file.cpp:10:3: error: 'PrintAddress' is a private member of 'tBar' PrintAddress(thing); source_file.cpp:42:6: note: in instantiation of function template >specialization 'tBar::PrintDataAndAddress' requested here bar.PrintDataAndAddress(tFoo()); // Compilation error source_file.cpp:17:7: note: declared private here void PrintAddress(const T& thing)

aber nur in Clang ++. GCC und MSVC sind in Ordnung mit ihm (man schnell, dass in http://rextester.com/l/cpp_online_compiler_clang durch Einfügen diesen Code testen)

Es scheint, als ob tBar::PrintDataAndAddress<tFoo>(const tFoo&) wird den gleichen Zugang wie tFoo verwenden, wo es befreundete wird. Ich weiß das, weil tFoo in tBar befreundet dieses Problem behebt. Das Problem verschwindet auch, wenn tBar::PrintDataAndAddress eine Nicht-Template-Funktion ist.

Ich konnte im Standard nichts finden, das dieses Verhalten erklärt. Ich glaube, es könnte eine schlechte Interpretation von 14.6.5 - temp.inject sein, aber ich kann nicht behaupten, dass ich alles davon gelesen habe.

Weiß jemand, ob Clang richtig ist, den obigen Code nicht zu kompilieren? Können Sie bitte den entsprechenden C++ - Standardtext zitieren, wenn dies der Fall ist?

Es scheint, dass für dieses Problem das private Element, auf das zugegriffen wird, eine Vorlagenfunktion sein muss. Beispiel: Wenn wir im obigen Beispiel PrintAddress zu einer Nicht-Template-Funktion machen, wird der Code ohne Fehler kompiliert.

+0

Vielleicht vermisse ich etwas, aber es scheint mir völlig vernünftig, dass clang das nicht kompiliert. Was ich merkwürdig finde, ist, dass die Friend-Deklaration auskommentiert, veranlasst dies zu kompilieren. Es scheint mir, als ob es sich nicht kompilieren sollte. Ich habe in der Vergangenheit schon einige Fälle gesehen, in denen gcc "zu lässige" Bugs in Bezug auf Freundschaft hat, wo eine Menge Templating involviert ist. Ich denke, das ist hier der Fall. –

+0

@NirFriedman warum denkst du es ist vernünftig, dass Clang das nicht kompiliert? Es scheint mir kontraintuitiv zu sein. Klasse A gibt einem Freund Zugriff auf eine Memberfunktion der Klasse B (in diesem Fall eine Template-Funktion), damit er Zugriff auf die privaten und geschützten Members von A erhält, aber warum sollte der Zugriff auf die privaten/geschützten Member von B entfernt werden? – TheProgammerd

+0

Sieht für mich wie ein Clang Bug aus. Suche nach, ob es bereits gemeldet wurde. – aschepler

Antwort

-3

Versuchen Sie, diese vor dem Freund Funktion Hinzufügen

template <typename tFoo> 
+0

Das behebt das Problem leider nicht. In der Tat, es macht es noch schlimmer, da jetzt alle Spezialisierungen von PrintDataAndAddress das gleiche Problem haben, was dazu führt, dass "bar.PrintDataAndAddress (tWidget());" und "bar.PrintDataAndAddress (tFoo());" den Kompilierungsfehler auslöst (bevor es für die tWidget Spezialisierung kompilieren würde) – TheProgammerd

+0

Welche Art von Compiler verwenden Sie? – felit

+0

Wie in der einleitenden Frage, Clang. Sie können einen Online-Clang-Compiler verwenden, um das Problem selbst zu testen. Klicken Sie hier für eine abgespeckte Version des obigen Codes von @ascheppler: rextester.com/YBBQ69572 – TheProgammerd

5

den Compiler erzwingen tBar::PrintDataAndAddress<tFoo> zu instanziiert, bevor Sie es das Problem löst.

int main() 
{ 
    tBar bar; 
    bar.PrintDataAndAddress(tWidget()); // Fine 

    auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work.... 

    bar.PrintDataAndAddress(tFoo()); // Now fine 

    return 0; 
} 

Es scheint ein Compiler promlem zu sein, wie es dies ganz ähnlich aussieht:

In C++, why isn't it possible to friend a template class member function using the template type of another class?

ein wenig genauer zu sein ... In der Zeile bar.PrintDataAndAddress(tFoo()); der Compiler instanziieren hat Die Memberfunktion tBar::PrintDataAndAddress<tFoo> und gleichzeitig muss die Friend-Deklaration aufgelöst werden. Das sind intern zwei getrennte Schritte. Scheinbar tut der Compiler es nicht in der richtigen Reihenfolge, wenn es in einen Ausdruck geschrieben wird. Um zu erzwingen, dass der Compiler zuerst bar.PrintDataAndAddress(tFoo()) durch Zugriff auf den Funktionszeiger initialisiert, sind diese beiden Schritte in der richtigen Reihenfolge.

+0

+1 für das Finden einer Problemumgehung nicht zugegriffen werden können. Ihre Theorie erklärt nicht vollständig das Verhalten, das wir sehen (wie die Tatsache, dass dies behoben ist, wenn 'tBar' sich mit' tFoo' anfreunden kann oder wenn 'PrintAddress' keine Template-Funktion ist), aber das ist das bisher Nächste. – TheProgammerd

Verwandte Themen