2016-10-31 15 views
16

Proszę spojrzeć na ten fragment kodu. Wiem, że to nie ma sensu, to tylko w celu zilustrowania problemu jestem napotykają:Funkcja szablonu członka klasy A zadeklarowana jako przyjaciel w klasie B nie może uzyskać dostępu do prywatnych członków klasy A (tylko 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; 
} 

Powyższy kod powoduje następujący błąd:

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)

ale tylko w Clang ++. GCC i MSVC są w porządku z nim (można szybko sprawdzić, wklejając ten kod w http://rextester.com/l/cpp_online_compiler_clang)

Wydaje się jakby tBar::PrintDataAndAddress<tFoo>(const tFoo&) wykorzystuje taki sam dostęp jak tFoo, gdzie jest zaprzyjaźniony. Wiem o tym, ponieważ zaprzyjaźnienie się z tFoo w rozwiązuje ten problem. Problem również ustępuje, jeśli tBar::PrintDataAndAddress jest funkcją inną niż szablonowa.

Nie znalazłem w standardzie żadnych informacji wyjaśniających to zachowanie. Wierzę, że może to być zła interpretacja 14.6.5 - temp.inject, ale nie mogę twierdzić, że przeczytałem to wszystko.

Czy ktoś wie, czy Clang ma rację nie skompilować powyższy kod? Czy możesz podać odpowiedni tekst standardowy C++, jeśli tak jest?

Wygląda na to, że aby ten problem mógł się pojawić, dostęp do prywatnego elementu musi być funkcją szablonu. np. w powyższym przykładzie, jeśli stwierdzisz, że PrintAddress jest funkcją inną niż szablon, kod skompiluje się bez błędów.

+0

Może brakuje mi czegoś, ale wydaje mi się całkowicie rozsądne, że klang nie kompiluje tego. Dziwne jest to, że deklaracja przyjaciela skomentowała, powoduje to kompilację. Wydaje mi się, że nie powinno się kompilować, niezależnie. W przeszłości spotkałem się z kilkoma przypadkami, w których gcc ma "zbyt łagodne" błędy w odniesieniu do przyjaźni, w których bierze udział wiele szablonów, tak jest w tym przypadku. –

+0

@NirFriedman dlaczego uważasz, że rozsądne jest, że Clang tego nie kompiluje? Wydaje mi się to sprzeczne z intuicją. klasa A daje znajomemu dostęp do funkcji członkowskiej klasy B (funkcja szablonu w tym przypadku), więc ma dostęp do prywatnych i chronionych członków A, ale dlaczego miałaby ona usunąć dostęp do prywatnych/chronionych członków B? – TheProgammerd

+0

Wygląda mi jak błąd w klangach. Wyszukiwanie, czy jest już zgłoszone. – aschepler

Odpowiedz

-3

spróbuj dodać tę funkcję przed znajomego

template <typename tFoo> 
+0

To nie rozwiązuje problemu, niestety. W rzeczywistości jest to gorsze, ponieważ teraz wszystkie specjalizacje PrintDataAndAddress będą miały ten sam problem, powodując zarówno 'bar.PrintDataAndAddress (tWidget());', jak i 'bar.PrintDataAndAddress (tFoo());' w celu wywołania błędu kompilacji (zanim skompiluje się dobrze dla specjalizacji tWidget) – TheProgammerd

+0

Jakiego typu kompilatora używasz? – felit

+0

Jak stwierdzono w pytaniu otwierającym, Clang. Możesz użyć kompilatora online Clang, aby samodzielnie przetestować problem. Kliknij tutaj, aby zapoznać się ze skróconą wersją powyższego kodu podaną przez @aschepler: rextester.com/YBBQ69572 – TheProgammerd

5

zmuszając kompilator instancji tBar::PrintDataAndAddress<tFoo> przed użyciem to rozwiązuje problem.

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

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

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

    return 0; 
} 

Wydaje się być promlem kompilator, jak to wygląda dość podobnie do tego:

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

Aby być trochę bardziej precyzyjny ... W linii bar.PrintDataAndAddress(tFoo()); kompilator musi instanciate funkcja członkowska tBar::PrintDataAndAddress<tFoo> i jednocześnie musi rozwiązać deklarację przyjaciela. To są wewnętrzne dwa osobne kroki. Wygląda na to, że kompilator nie robi tego w porządkowej kolejności, gdy jest napisany w jednym wyrażeniu. Aby zmusić kompilator do utworzenia egzemplarza bar.PrintDataAndAddress(tFoo()) najpierw przez dostęp do wskaźnika funkcji, te dwa kroki są we właściwej kolejności.

+0

+1 za znalezienie obejścia. Twoja teoria nie wyjaśnia w pełni zachowań, które widzimy (podobnie jak fakt, że jest to naprawione, jeśli 'tBar' zaprzyjaźnia się' tFoo' lub jeśli 'PrintAddress' nie jest funkcją szablonu), ale jest najbliższą nam rzeczą. – TheProgammerd