каково ожидаемое поведение? - PullRequest
7 голосов
/ 23 августа 2010

Ниже приведена чисто академическая иерархия классов.

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» (видимость по сравнению с доступностьюИКС).Однако 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 ошибочны.Это правильно?

Ответы [ 3 ]

2 голосов
/ 23 августа 2010

Я не думаю, что что-то из этого плохо сформировано.Во-первых, для using X::f2 ищется X, и это однозначно приведет к типу класса X.Затем ищется f2 в X, и это тоже недвусмысленно (в D! Это не ищется).

Второй случай будет работать по той же причине.

Но если вы вызовите f2 для объекта D, вызов будет неоднозначным, поскольку имя f2 ищется во всех подобъектах типа DX, D имеет два таких подобъекта, а f2 является нестатической функцией-членом.Та же самая причина имеет место для второго случая.Для этого не имеет значения, назовите ли вы f3, используя Z::X или X напрямую.Оба они обозначают класс X.

Чтобы получить неоднозначность для объявления использования, вам нужно написать его по-другому.Обратите внимание, что в C ++ 0x using ThisClass::...; недопустимо.Хотя это в C ++ 03, пока все имя относится к члену базового класса.

И наоборот, если бы это было разрешено в C ++ 0x, то все объявление using также было бы допустимым, поскольку 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, и задним числом реализуют эту формулировку:

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

Это означает, что неосновные классы считаются , но это ошибка, если назван не базовый класс.Если бы стандарт намеревался игнорировать неосновные имена классов, он сказал бы, что может только здесь, или излагает это явно (обе практики выполнены).Стандарт, однако, вовсе не является следствием использования , и , может .И GCC реализует формулировку C ++ 0x, потому что в противном случае он полностью отклоняет код C ++ 03, просто потому что объявление using содержит свое имя класса.

В качестве примера неясной формулировки рассмотрим следующее выражение:

a.~A();

Это синтаксически неоднозначно, поскольку это может быть вызов функции-члена, если a является объектом класса,но это может быть псевдо-деструктор-вызов (который не используется), если a имеет скалярный тип (например, int).Но в стандарте говорится о синтаксисе вызова псевдодеструктора и доступа к членам класса в 5.2.4 и 5.2.5 соответственно

Левая часть оператора точки должна быть скалярнойtype.

Для первой опции (точка) типом первого выражения (выражения объекта) должен быть «объект класса» (полного типа).

То естьнеправильное использование, потому что это не устраняет двусмысленность вообще.Он должен использовать «только», и компиляторы интерпретируют это таким образом.Это в основном исторические причины, как недавно сказал мне один из членов комитета по usenet.См. Правила составления и составления международных стандартов , Приложение H.

0 голосов
/ 23 августа 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.

0 голосов
/ 23 августа 2010

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

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

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

Z::X::f2 должно работать. Или Z::f2 должно работать.

...