Каково ожидаемое поведение?

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

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 ошибочны. Это правильно?

7
задан Chubsdad 23 August 2010 в 13:06
поделиться

3 ответа

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

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

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

Чтобы сделать объявление using неоднозначным, вам нужно написать его по-другому. Обратите внимание, что в C ++ 0x использование 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 , и к вложенному классу, найденному как Y :: Trouble . Имя проблема , предшествующая :: , ищется с использованием неквалифицированного поиска ( 3.4.1 / 7 , который делегирует 10.2 в первый маркер), игнорируя любые имена объектов, функций и перечислителей ( 3.4.3 / 1 - однако в данном случае таких имен нет). Затем это нарушает требование 10.2 о том, что:

Если результирующий набор объявлений не все из подобъектов одного и того же типа ... программа имеет неправильный формат.


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

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

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

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

a.~A();

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

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

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

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

2
ответ дан 7 December 2019 в 18:38
поделиться

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

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

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

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

0
ответ дан 7 December 2019 в 18:38
поделиться

Во-первых, чтобы прояснить ответ Йоханнеса. Когда вы говорите с использованием Z :: X :: f2; , компилятор не «строит путь» к f2 , чтобы отслеживать, как к нему следует обращаться. Поскольку Z :: X - это то же самое, что X , объявление точно такое же, как с использованием 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 до тошноты, потому что каждый класс вносит свое собственное имя в свою собственную область видимости. Таким образом, :: здесь не выражает наследование.

Теперь к решению проблемы. 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:

Следующие шаги определяют результат поиска имени в классе scope, C. Во-первых, каждое объявление для имя в классе и в каждом из его подобъекты базового класса обдуманный. … Если результирующий набор объявлений не все из подобъектов из того же типа, или в наборе есть нестатический член и включает элементы от различных подобъектов существует двусмысленность и программа плохо сформированный. В противном случае этот набор является результат поиска.

Обратите внимание, что я выделил множественное слово подобъекты жирным шрифтом. Хотя имя X встречается в двух подобъектах, они оба относятся к одному типу, а именно X .

0
ответ дан 7 December 2019 в 18:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: