2013-08-13 8 views
10

Za pomocą poniższego kodu clang 3.0 podaje error: lookup of 'N' in member access expression is ambiguous, natomiast clang 3.4 i gcc 4.8 akceptują kod bez błędu.Czy nazwa znaleziona w zależnej klasie bazowej powinna ukryć nazwę obszaru nazw w punkcie tworzenia instancji?

struct B 
{ 
    struct M 
    { 
     void f() 
     { 
     } 
    }; 
}; 

namespace N 
{ 
    struct M 
    { 
     void f() 
     { 
     } 
    }; 
} 

template<typename> 
struct A : N::M, B::M 
{ 
    typedef B N; 
}; 

struct D : A<int> 
{ 
    A<int> m; 
    void f() 
    { 
     m.N::M::f(); // found class-name 'A<int>::N' (unambiguous) 
    } 
}; 

template<typename T> 
struct C : A<T> 
{ 
    A<T> m; 
    void f() 
    { 
     m.N::M::f(); // found namespace-name 'N' (ambiguous?) 
    } 
}; 

template struct C<int>; 

Po konsultacji standard nie jest dla mnie jasne, której zachowanie jest prawidłowe w odniesieniu do ekspresji w C<T>::f().

Ponieważ N szuka się zarówno w zakresie klasy ekspresji obiektu n (która zależy), w związku z ekspresją cały przyrostkiem (czyli zakres funkcji C<T>::f()), konieczne jest, aby opóźnić wyszukiwanie do momentu wystąpienia.

W momencie wystąpienia wyszukiwanie będzie niejednoznaczne, jeśli znajdzie zarówno przestrzeń nazw N, jak i typedef A<T>::N. Deklaracja N jest widoczna tylko wtedy, gdy nie jest ukryta przez deklarację A<T>::N.

Pytanie brzmi, czy nazw N należy uznać za ukryte przez typedef A<T>::N gdy patrząc N „w kontekście całego postfix ekspresji” i „w punkcie definicji szablonu”.

Cyt z projektu C++ roboczego w N3242 = 11-0012 (luty 2011): dostęp element

3.4.5 klasy [basic.lookup.classref]

Jeśli identyfikator ekspresja w sposób Użytkownik ma dostęp klasa wykwalifikowanym ID formy

class-name-or-namespace-name::...

the-or-nazw-nazwa klasy nazwa następnego operator . lub -> jest spojrzał w górę zarówno na w kontekście całego Postfix-expression oraz w zakresie klasy obiektu wyrażenia. Jeśli nazwa zostanie znaleziona tylko w zakresie zakresu klasy wyrażenia obiektowego, nazwa będzie odnosić się do nazwy klasy. Jeśli nazwa zostanie znaleziona tylko w kontekście całego wyrażeń postfiksowych, nazwa będzie odnosić się do nazwy klasy lub nazwy przestrzeni nazw. Jeśli nazwa znajduje się w obu kontekstach, nazwa-klasy-lub-nazwa-przestrzeni-nazwy powinna odnosić się do tej samej jednostki .

14.6.4 rozdzielczości nazwa Zależnie [temp.dep.res]

w nim nazwy zależne nazwy z następujących źródłach uważa:

- oświadczenia, jakie są widoczne w punkcie definicji szablonu.

- Deklaracje z przestrzeni nazw powiązanych z typami argumentów funkcji zarówno z kontekstu tworzenia instancji (14.6.4.1), jak iz kontekstu definicji.

+2

Jak można wymyślić taki kod? – jrok

+1

@jrok: za dużo wolnego czasu –

+0

tego rodzaju rzeczy pojawia się * cały czas * podczas pisania parsera C++;) – willj

Odpowiedz

5

To jest coś, co zostało zmienione w C++ 11.Tekst, który podałeś pochodzi z C++ 03; przed C++ 11 było to niejednoznaczne, ponieważ zastosowano oba wyszukiwania i był to błąd, jeśli znaleziono inne nazwy . W C++ 11, tekst jest:

Jeśli identyfikator ekspresja w dostępie członkiem klasy jest wykwalifikowanym-id z formy klasa nazwa-lub-namespace-name :: ... The nazwa-klasy-lub-nazwa-przestrzeni po znaku. lub -> operator jest najpierw sprawdzony w klasie wyrażenia obiektu i użyta jest nazwa , jeśli została znaleziona. W przeciwnym razie zostanie on sprawdzony w kontekście całego wyrażenia postfiksowego. [Uwaga: Patrz 3.4.3, , który opisuje wyszukiwanie nazwy przed ::, która tylko znajdzie typ lub nazwę przestrzeni nazw. -end note]

Zasadniczo to uprawnia wyszukiwanie w zakresie klasy, a nie robi drugiego, jeśli nazwa zostanie znaleziona.

dlaczego ten tylko wpływa szablon w starszych wersjach standardu: Myślę (trudno być pewni niczego tutaj), że to dlatego, że w przypadku braku szablonu odnośnika w kontekście całego postfiksowego wyrażenia również znajduje typedef w klasie bazowej, więc oba wyszukiwania rozstrzygają dla tej samej encji. W przypadku szablonu, wyszukiwanie w całym wyrażeniu Postfiks nie bierze pod uwagę zależnej klasy bazowej i znajduje tylko przestrzeń nazw N. Jednak po utworzeniu instancji C wyszukiwanie w zakresie wykrywa typedef. Ponieważ dwa wyszukiwania znajdują różne jednostki, powiązanie nazwy jest niejednoznaczne.

+0

Wygląda na to, że muszę uaktualnić moją kopię standardu! – willj

+0

Ta zmiana była w N3242-> N3291, tuż przed ostatecznym standardem C++ 11. Na szczęście zmiana upraszcza wiele strasznie skomplikowanych zasad wyszukiwania nazw w wyrażeniu dostępu do elementu klasy. – willj

+0

@willj Nie zrobiłem porównania osobiście, ale powiedziano mi, że [N3337] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) , pierwszy szkic po C++ 11, jest bliższy wydanemu standardowi zawartości niż N3242. – Casey