какво е очакваното поведение?

По-долу е чисто академично измислена йерархия на класовете.

struct X{
        void f1();
        void f2();
        void f3();
};

struct Y : private X{
        void f4();
};

struct Z : X{
};

struct D : Y, Z{
        using X::f2;
        using Z::X::f3;
};

int main(){}

Очаквах използването на декларация за X::f2 да бъде двусмислено, тъй като „X“ е двусмислена основа на „D“ (видимост срещу достъпност на X). Въпреки това g++ (ideone.com) го компилира добре.

Проверих с Online Comeau и дава грешка при използване на декларация за X::f2, както се очаква. Въпреки това дава неяснота при използването на декларация и за Z::X::f3.

И така, какво е очакваното поведение?

Редактиране 1:

Моля, препратка към съответния раздел на стандарта би била полезна.

Редактиране 2:

Проверих с VS 2010 и има възражения само с използващата декларация X::f2. Не става дума обаче за неяснота на 'X' (както в случая с gcc и Comeau). Става въпрос за "грешка C2876: 'X': не всички претоварвания са достъпни".

Редактиране 3:

struct X{
    void f(){}
};

struct Y : X{
    struct trouble{
        void f(){}
    };

};

struct trouble : X{
};

struct letscheck : Y, trouble{
    using trouble::f;
};

int main(){}

Тук се опитах (целенасочено) да създам проблем с типовете при използване на декларация. Gcc все още компилира това добре, както и VS2010. Comeau все още дава грешка (както се очаква) относно двусмислените типове „проблеми“. Следвайки обясненията, дадени за първоначалните заявки, изглежда, че GCC и VS2010 грешат. Вярно ли е?


person Chubsdad    schedule 23.08.2010    source източник
comment
В D добавете метод, който извиква f2(), вижте какво ще се случи.   -  person Martin York    schedule 23.08.2010


Отговори (3)


Не мисля, че някой от тях е неправилно оформен. Първо, за using X::f2 се търси X и това недвусмислено ще даде тип клас X. След това се търси f2 в X и това също е недвусмислено (не се търси в D!).

Вторият случай ще работи по същата причина.

Но ако извикате f2 на D обект, извикването ще бъде двусмислено, защото името f2 се търси във всички подобекти на D от тип X, а D има два такива подобекта, а f2 е нестатична функция член. Същата причина важи и за втория случай. За това няма значение дали наименувате f3, използвайки Z::X или X директно. И двете обозначават класа X.

За да получите неяснота за декларацията за използване, трябва да я напишете по различен начин. Имайте предвид, че в C++0x using ThisClass::...; не е валиден. Въпреки това е в C++03, стига цялото име да се отнася до член на базовия клас.

Обратно, ако това би било разрешено в C++0x, цялата декларация за използване също би била валидна, тъй като C++0x не взема предвид подобектите за търсене по име: D::f2 недвусмислено се отнася само до една декларация (този в X). Вижте DR #39 и окончателния документ N1626.

struct D : Y, Z{
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class
    using D::f2;
 
    // still fine (if not referred to by calls/etc) :)
    using Z::X::f3;
};

struct E : D {
  // ambiguous in C++03
  // fine in C++0x (if not referred to by an object-context (such as a call)).
  using D::f2;
};

Стандартът C++03 описва това в параграфи 10.2 и 3.4.3.1.


Отговор за Edit3:

Да, GCC и VS2010 грешат. trouble се отнася до типа, намерен от името на инжектирания клас от ::trouble и до вложения клас, открит като Y::trouble. Името trouble, предшестващо ::, се търси с помощта на неквалифицирано търсене (от 3.4.1/7, което делегира на 10.2 в първия куршум), като се игнорират всички имена на обекти, функции и изброители (3.4.3/1 - в този случай обаче няма такива имена). След това нарушава изискването на 10.2, че:

Ако полученият набор от декларации не са всички от подобекти от един и същи тип ... програмата е неправилно оформена.


Възможно е VS2010 и GCC да интерпретират формулировката на C++0x по различен начин от Comeau и със задна дата прилагат тази формулировка:

В декларация за използване, използвана като декларация за член, спецификаторът на вложеното име трябва да наименува базов клас на дефинирания клас.

Това означава, че небазовите класове са взети под внимание, но е грешка, ако се наименува небазов клас. Ако стандартът възнамерява да игнорира имена на неосновни класове, тук ще каже може само или ще го изпише изрично (и двете практики са готови). Стандартът обаче изобщо не е последователен с използването на трябва и може. И GCC прилага C++0x формулировката, защото отхвърля иначе напълно фин C++03 код, само защото използващата декларация съдържа неговото име на клас.

За пример на неясна формулировка разгледайте следния израз:

a.~A();

Това е синтактично двусмислено, защото може да бъде извикване на членска функция, ако a е обект от клас, но може да бъде извикване на псевдо-деструктор (което е no-op), ако a има скаларен тип (като int). Но това, което стандартът казва, е за синтаксиса на извикване на псевдодеструктор и достъп на член на класа съответно на 5.2.4 и 5.2.5

Лявата страна на оператора точка трябва да бъде от скаларен тип.

За първата опция (точка) типът на първия израз (обектният израз) трябва да бъде „обект от клас“ (от пълен тип).

Това е грешна употреба, защото изобщо не изяснява неяснотата. Трябва да използва само can и компилаторите го интерпретират по този начин. Това има предимно исторически причини, както наскоро ми каза един член на комисия в usenet. Вижте Правилата за структурата и съставянето на международните стандарти, Приложение H.

person Johannes Schaub - litb    schedule 23.08.2010
comment
@litb: Модифицирах клас 'D', както следва: struct D : Y, Z{ използвайки X::f2; използвайки Z::X::f3; void f(){X *p = this;} }; Сега получих грешката prog.cpp:19: error: repeated using declaration 'using X::f3' prog.cpp: In member function 'void D::f()': prog.cpp:20: error: 'X ' е двусмислена основа на 'D' Различни ли са правилата за 'използване на декларация' по време на 'търсене на име' - person Chubsdad; 23.08.2010
comment
@chubsdad всичко, което мога да направя, е да повторя стандарта. Не знам какво прилагат тези компилатори. Не виждам декларация за повторно използване във вашия код. - person Johannes Schaub - litb; 23.08.2010
comment
@litb: лека правописна грешка в кода ми и следователно изхода на компилатора. Ревизираната грешка следва: prog.cpp: Във функция-член „void D::f()“: prog.cpp:20: грешка: „X“ е двусмислена основа на „D“ - person Chubsdad; 23.08.2010
comment
@chubsdad препоръчвам ви да прочетете 10.2 в C++03, за да разберете какво се случва. Няма преобразуване, ако напишете декларация за използване (резултатът от търсенето просто ще бъде двусмислен). И няма преобразуване в C++03, ако потърсите f2 по-късно в D за повикване, което би било неправилно оформено. В C++0x се извършва преобразуване, което е необходимо за ревизираната формулировка на 10.2, която е описана в 5.2.5/5 в FCD, за да се диагностицира правилото за множество подобекти. В C++0x това не е двусмислено търсене, а двусмислено преобразуване, това е грешката. - person Johannes Schaub - litb; 23.08.2010
comment
@chubsdad добавих някои забавни неща. Надявам се, че нямате нищо против. Това също трябва да помогне за малко изясняване на нещата, надявам се :) - person Johannes Schaub - litb; 23.08.2010
comment
@litb: Моля те да се уча от всички гурута тук. А ученето със „забавление“ е най-доброто блаженство. Между другото, защо изтрихте този коментар, свързан с „тип и множество подобекти“. Това беше коментарът „направо“. - person Chubsdad; 23.08.2010
comment
@chubsdad реших, че коментарът не е точен и ще трябва да го изпратя отново (заменяйки обекта с нестатична функция или член на данните), но след това реших, че ще е по-добре да оставя @Potatoswatter да отговори :) - person Johannes Schaub - litb; 23.08.2010
comment
@litb,@potatoswatter: Аз също се опитах да се забавлявам малко и създадох Редактиране 3. Моля, проверете и потвърдете разбирането ми от всички коментари от вас. - person Chubsdad; 23.08.2010
comment
@chubsdad, моля, имайте предвид, че potatoswatter няма да получи това съобщение, защото той не се появи в тази нишка за коментари. Само първият псевдоним, който се показва след '@' и само псевдонимите, които участват в тази нишка, ще получават известия. Не харесвам това поведение обаче :( - person Johannes Schaub - litb; 23.08.2010
comment
@litb: опитвам се да поддържам темпото тук с теб. Изгубих се при Това означава, че се разглеждат не-базови класове, но е грешка, ако се наименува не-базов клас. Как се стига до това заключение? - person Chubsdad; 23.08.2010
comment
@chubsdad вижте дискусията за подобен проблем тук: groups.google .com/group/comp.std.c++/browse_thread/thread/ . Редакторът на Standard Пийт Бекер изяснява добре въпроса в края. - person Johannes Schaub - litb; 23.08.2010

използвайки X::f2; не трябва да работи поради частно наследяване на кода по-долу

struct Y : private X{
    void f4();
};

Не е възможно да се осъществи достъп до членове на X през Y. Така че X::f2 би бил в конфликт.

Z::X::f2 трябва да работи. Или Z::f2 трябва да работи.

person bjskishore123    schedule 23.08.2010
comment
C++ има правилото за множествен достъп. Ако едно име може да бъде намерено по множество пътища в йерархията на базовия клас и един от тях е публичен, този път за обществен достъп се взема. Вижте 11.7/1. - person Johannes Schaub - litb; 23.08.2010
comment
@litb: Това означава, че „използването на X::f2“ е добре, тъй като „X“ е достъпно чрез „Z“ в OP. Но тогава „X“ е двусмислено и следователно грешка, свързана с неяснотата. Така че съобщението за грешка на VS2010 относно „не всички претоварвания са достъпни“ вероятно е сложно. Това означава, че Комо е прав. Най-малко подозирах грешката относно използването на декларация за 'Z::X::f3'. - person Chubsdad; 23.08.2010
comment
@chubsdad това просто означава, че не е грешка в достъпността. Променя достъпа до пътя, който дава най-голям достъп. Това не би било двусмислено търсене дори без 11.7/1, но тогава нямаше да знаем дали трябва да приложим частна или публична достъпност. - person Johannes Schaub - litb; 23.08.2010

Първо, за да изясня отговора на Йоханес. Когато кажете using Z::X::f2;, компилаторът не "изгражда път" до f2, за да следи как трябва да бъде достъпен. Тъй като Z::X е същото нещо като X, декларацията е точно същата като казването на using X::f2;. Сравнете го с този пример:

struct A { void f() {} void g() {} };
struct B { void f() {} void g() {} };
struct C { typedef A X; };
struct D { typedef B X; };
struct E : A, B {
    using C::X::f; // C::X == A
    using D::X::g; // D::X == B
};

Синтаксисът Z::X работи не поради наследяване или членство, а защото идентификаторът X е достъпен от обхвата Z. Позволено ви е дори да пишете Z::Z::Z::Z::X::X::X::X ad nauseam, защото всеки клас носи собствено име в своя собствен обхват. Следователно :: тук не изразява наследяване.

Сега да решим проблема. f2 е наследено от Y и Z от X. По този начин той е първокласен член на Y и Z. E не трябва да знае за X, защото това е скрита подробност за изпълнение. Значи искаш

struct D : Y, Z{
    using Y::f2; // error: inaccessible
    using Z::f3;
};

За да обясня по отношение на 9.1/2, както питате:

Името на клас се вмъква в обхвата, в който е декларирано, веднага след като се види името на класа. Името на класа също се вмъква в обхвата на самия клас; това е известно като име на инжектиран клас.

Името X се инжектира в X като X::X. След това се наследява в Y и Z. Y и Z не декларират имплицитно X в собствения си обхват.

10.2/2:

Следващите стъпки дефинират резултата от търсенето на име в обхват на клас, C. Първо се разглежда всяка декларация за името в класа и във всеки от неговите подобекти на базов клас. … Ако полученият набор от декларации не са всички от подобекти от един и същи тип или наборът има нестатичен член и включва членове от различни подобекти, има неяснота и програмата е зле оформени. В противен случай този набор е резултат от търсенето.

Обърнете внимание, че удебелих подобектите на думата в множествено число. Въпреки че името X се намира в два подобекта, и двата са от един и същи тип, а именно X.

person Potatoswatter    schedule 23.08.2010
comment
В обхвата на „D“ има два подобекта „X“ (един от „Y“ (недостъпен) и един от „Z“ (достъпен)). По отношение на $9.2 (Име на инжектиран клас). Не трябва ли „използването на X::f2“ да е двусмислено с горната логика, тъй като „X“ е двусмислена основа на „D“? - person Chubsdad; 23.08.2010
comment
@chubs: Има само един клас с име X. X назовава клас, а не подобект: това е смисълът на моята илюстрация. - person Potatoswatter; 23.08.2010
comment
Благодаря за разясненията. Редактирах въпроса (Редактиране 3) и исках вашето мнение според моето разбиране. - person Chubsdad; 23.08.2010