2016-04-21 15 views
12

mam ten kod (problem diament):Diamond of śmierci i operatora rozdzielczość zakres (w C++)

#include <iostream> 
using namespace std; 

struct Top 
{ 
    void print() { cout << "Top::print()" << endl; } 
}; 

struct Right : Top 
{ 
    void print() { cout << "Right::print()" << endl; } 
}; 

struct Left : Top 
{ 
    void print() { cout << "Left::print()" << endl; } 
}; 

struct Bottom: Right, Left{}; 

int main() 
{ 
    Bottom b; 
    b.Right::Top::print(); 
} 

chcę zadzwonić print() w Top klasie.

Kiedy próbuję ją skompilować, pojawia się błąd: 'Top' is an ambiguous base of 'Bottom' w tej linii: b.Right::Top::print(); Dlaczego jest niejednoznaczny? Wyraźnie określiłem, że chcę Top od Right, a nie od Left.

Nie chcę wiedzieć JAK to zrobić, tak, można to zrobić za pomocą odniesień, dziedziczenia wirtualnego, itp. Chciałbym tylko wiedzieć, dlaczego jest niejednoznaczne, ponieważ jest b.Right::Top::print();.

+0

Jest to niejednoznaczne przez * "Jeśli operator dostępu do elementu klasy, w tym niejawne" to-> ", jest używane w celu uzyskania dostępu do niestatycznego elementu danych lub niestatycznej funkcji elementu, odniesienie jest źle sformułowane, jeśli lewy operand (uważany za wskaźnik w "."Przypadek operatora" nie może zostać niejawnie przekonwertowany na wskaźnik do klasy nazw prawego operandu "*, 11.2p6. Zauważ, że klasa nazewnictwa to' A', ale 'D *' nie może być niejawnie przekształcona w 'A *'. –

+0

Semantyka polega na tym, że mówisz, jaką funkcję chcesz wywołać za pomocą 'B :: A :: tell' .Pomóż kompilatorowi w użyciu' D :: tell', np. Przy wyszukiwaniu nazw. Podaj podobiekt, którego musi użyć - będzie miał dwie opcje: Zejście w dół do "A" ponad "B" lub przez "C", i da ci błąd –

+0

Istnieją dwie "poważne" kontrole niejednoznaczności które są wykonywane w kontekstach działających na obiektach w czasie wykonywania: jeden w wersji 5.2.5p5 i taki, który cię gryzie tutaj w 11.2p6. Ten w 5.2.5p5 odrzuca 'd.tell()' jeśli chcesz usunąć wszystkie funkcje tell z wyjątkiem jednego z 'A', ponieważ klasą nazewnictwa jest' D', ale 'tell' będzie bezpośrednim członkiem' A', a 'A' jest niejednoznaczne .Jeśli następnie powiesz' DB :: A :: tell() ', jest dobrze uformowany przez 5.2.5p5 , ale źle sformułowany przez 11.2p6. Kontrole te uzupełniają się i są ważne dla prawidłowego działania w systemie typu. –

Odpowiedz

15

Why is it ambiguous? I explicitly specified that I want Top from Right and not from Left .

To była twoja intencja, ale tak się nie dzieje. Right::Top::print() jawnie nazywa funkcję członka, którą chcesz wywołać, czyli &Top::print. Ale nie określa, na którym podstronie b wywołujemy tę funkcję członka. Kod jest równoznaczne koncepcyjnie do:

auto print = &Bottom::Right::Top::print; // ok 
(b.*print)();        // error 

Część, która wybiera print jest jednoznaczna. Jest to niejawna konwersja z b na Top, która jest niejednoznaczna. Trzeba wyraźnie disambiguate jakim kierunku idziesz w, robiąc coś takiego:

static_cast<Right&>(b).Top::print(); 
+0

Więc dlaczego działa, jeśli używam 'Right :: print()' zamiast 'Right :: Top :: print() '? Żadna z nich nie działa z wskaźnikiem na element, ale "b.Right :: print()" zadziała. (oczywiście usunąłem 'print()' w klasie 'Right'). – PcAF

+1

@PcAF Ponieważ konwersja z 'b' na' Right' jest jednoznaczna. Istnieje tylko jeden podobiekt typu "Right". – Barry

+0

Dziękujemy za odpowiedź. Jeśli konwersja z 'b' na' Right' jest jednoznaczna, dlaczego: 'auto print = i Bottom :: Right :: print;' '(b. * Print)()' podaje błąd 'niejednoznacznej bazy' (Right is empty class)? – PcAF

4

Zakres operator rozdzielczości jest lewostronne (choć nie pozwala nawiasów).

Więc natomiast chcesz zapoznać się A::tell wewnątrz B, id wyrażenie odnosi się do tell wewnątrz B::A, który jest po prostu A, który jest niejednoznaczna.

Obejście problemu polega na pierwszym rzuceniu do jednoznacznej bazy B, a następnie ponownym przesłaniu go do A.

Język prawniczych:

[basic.lookup.qual]/1 mówi,

The name of a class or namespace member or enumerator can be referred to after the :: scope resolution operator applied to a nested-name-specifier that denotes its class, namespace, or enumeration.

Odpowiedni gramatyka dla zagnieżdżonego-NAME specyfikatorem jest

nested-name-specifier:

    type-name::

    nested-name-specifieridentifier::

Pierwszy specyficzny dla nazwy zagnieżdżonej znak to B::, a A został sprawdzony w ciągu to. Następnie B::A jest specyfikatorem zagnieżdżonym oznaczającym A, a wewnątrz niego znajduje się tell.

Podobno MSVC akceptuje przykład. Prawdopodobnie ma niestandardowe rozszerzenie, aby rozwiązać niejednoznaczność, przechodząc przez takie specyfikatory.

+0

Proszę zacytować odpowiednie standardy, bo to spekulacja. –

+0

@ Cheersandhth.-Alf Tag [language-lawyer] został dodany po opublikowaniu. Zobaczę, czy potrafię wytłumaczyć dobre wyjaśnienie, ale zauważ, że po prostu niemożliwe jest wyszukiwanie od prawej do lewej. – Potatoswatter

+0

@Potatoswatter +1 i zobacz moje komentarze do pytania dotyczące referencji. –