On-line: гостей 1. Всего: 1 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 28.07.12 14:59. Заголовок: Причуды MS VC++ 2010 при поиске уточненых ( elaborated ) имен


Расскажу о странностях компилятора MS VC++ 2010 при поиске уточненных имен. Возможно я что-то сам не так понимаю,но тем не менее очевидно, что компилятор MS VC++ 2010 ведет себя не адекватно.

Рассмотрим такой простой пример.

#include  <iostream> 

namespace N1
{
struct Outer { int x; };
namespace N2
{
void f( struct Outer * );
}
}

void N1::N2::f( Outer *p )
{
std::cout << "sizeof( *p ) = " << sizeof( *p ) << std::endl;
std::cout << "sizeof( Outer ) = " << sizeof( Outer ) << std::endl;
}

int main()
{
N1::N2::f( 0 );
}


Самое интересное, что код компилируется. Но при этом компилятор подсвечивает красным цветом некоторые символы коды, и если подвести туда курсор, то высвечивает сообщение об ошибке.

Теперь о причудах компилятора.

У меня красным выделено имя пространства имен N1 в определении функции f. Если подвести курсор в эту позицию,то компилятор высвечивает два(!) объявления функции:
 
void N1::N2::f( N1::Outer *p )

и
void N1::N2::f( N1::N2::Outer *p )


Такое впечатление, что он не может определиться, какой из указанных им двух функций принадлежит данное определение функции f. Как видно в первом случае он трактует имя Outer, как принадлежащее пространству имен N1, а во втором случае - как принадлежащее пространству имен N1::N2

Когдаже курсор подводиться к вызову функции,то высвечивается сообщение "Error: существует более одного экземпляра перегруженной функции N1::N2::F соответствующей списку аргументов".


Но самое забавное начинается тогда, когда в определении функции подводишь курсор .к типу параметра функции,то есть к Outer Тогда компилятор показывает,что это имя относится к struct N1::N2::Outer.. Когда же курсор подводишь к имени параметра,то есть к p, компилятор указывает,что это имя есть N1::Outer*p.

Понимаете,о чем я веду речь? Естьопределение функции, у которой заголовок следующий:

void N1::N2::f( Outer *p ) 
{
//
}


Если подвести курсор к Outer в этом определении,то показывает, что это struct N1::N2::Outer. Аесли переместить курсор правее на имя p,то P ужеоказывается имеет тип N1::Outer *p

Вот такие чудеса! Очевидно, что это баг самого компилятора. Другое дело, что хочется понять, чем именно вызвано такое неадекватное поведение компилятораЮ то есть как при этом "мыслит" компилятор. Причина неадекватного поведения компилятора кроется в присутствии в объявлении функции f уточненного, или, как написано в стандарте С++ elaborated class type:

void f( struct Outer * );


Но как я поинмаю это не есть объявление нового имени в пространстве имен N1::N2, а это ссылка на объявление в пространстве имен N1. В противном случае компилятор вообще не должен компилироватькод, так как использование в теле функции f предложения


   std::cout << "sizeof( Outer ) = " << sizeof( Outer ) << std::endl;


было бы некорректным, так как тип Outer был бы не полным, а потому к нему нельзя применить оператор siuzeof

Если у кого-то есть какие-то соображения по этому поводу,то интересно было бы их услышать.
Так же интересно, как компилируется этот код другими компиляторами. Япробовал его компилировать старым Borland Builder C++ 5.0, и он естественно скомпилировался без проблем. Но, как известно, Borland C++ Builder сам страдает несоответствием стандарту С++, поэтому на него ориентироваться можноочень условно.

Как другие компиляторы ведут себя по отношению к указанному коду? Можетбытьони выдают какие-то предупреждения, которые позволяют понять недостатки кода?


Спасибо: 0 
ПрофильЦитата Ответить
Ответов - 9 [только новые]





ссылка на сообщение  Отправлено: 28.07.12 15:02. Заголовок: >>Но как я п..


>>Но как я поинмаю это не есть объявление нового имени в пространстве имен N1::N2, а это ссылка на объявление в пространстве имен N1.

Неправильно. Такое объявление может привести к неявному объявлению 'Outer' в enclosing namespace.

Предлагаю помедитировать на таким кодом:

void f(struct A*);
void f(A*);

и все-таки почитать стандарт, а не пытаться гадать на кофейной гуще...



Спасибо: 0 
Цитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:03. Заголовок: 7 пишет: >>..


7 пишет:

 цитата:
>>Но как я поинмаю это не есть объявление нового имени в пространстве имен N1::N2, а это ссылка на объявление в пространстве имен N1.

Неправильно. Такое объявление может привести к неявному объявлению 'Outer' в enclosing namespace.

Предлагаю помедитировать на таким кодом:

void f(struct A*);
void f(A*);

и все-таки почитать стандарт, а не пытаться гадать на кофейной гуще...



Почему вы так уверены, что в void f( struct A * ); struct A - есть объявление нового типа, а не ссылка на уже объявленный тип?
Есть разница между двумя объявлениями:

struct A;
struct A


Обратите внимание, что во втором случае точки с запятой нет. Согласно стандарту, который вы мне советуете почитать, между этими двумя объявлениями существует большая разница. В первом случае имя A вводится в область видимочсти как новый тип. Во-втором случае, когда нет точки с запятой, то есть как в примере с функцией, когда объявляется параметр, компилятор должен сначала смотреть, не объявлен ли уже данный тип в области видимости. И если такой тип уже объявлен, то он считает это уточненое имя как ссыку на ранее объявленное имя.
Вы наверное не внимательно читали мое первое сообщение. Если принять во внимание, что ваше утверждение верное, то как написано в моем исходном сообщении, крд не должен компилироваться! Так как оператор sizeof нельзя применять к неполным типам, проще говоря оператор sizeof не знает размер структуры, которая указана в качестве параметра функции в виде struct A *p. Чему равен размер strut A ?
Но тем не менее, о чем я сказал в исходном сообщении, код компилируется! И оператор sizeof выводит размер структуры, объявленной в пространстве имен N1. То есть компилятор правильно (на мой взгляд ссылается на нужны тип).

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

Если вы не согласны, то хотелось бы услышать обоснование вашего утверждения на основе стандарта.
Этот вопрос лостаточно не тривиальный, и вполне объяснимо, что возмодна неправльная трактовка тем или иным программистом.
Также обратите внимание на тело функции, где выводится на консоль значение оператора sizeof

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:06. Заголовок: Сыроежка пишет: Поч..


Сыроежка пишет:

 цитата:
Почему вы так уверены, что в void f( struct A * ); struct A - есть объявление нового типа, а не ссылка на уже объявленный тип?



Объявление функции 'void f( struct A * );' _может_ приветсти в объявлению типа 'A' в namespace, содержащем эту функцию. Поэтому вот этот код является well-formed:

void f(struct A*);
void f(A*);

int main(){}

согласно 3.3.2/6. Но если в процессе выполнения name lookup для идентификатора в elaborated-type-specifier будет определено что объявление с таким именем уже присутствует, то такая конструкция _не_ приведет к объявлению нового типа, а будет ссылаться на существующее объявление (7.1.6.3/2, 3.4.4/2).

Сыроежка пишет:

 цитата:
Есть разница между двумя объявлениями:

struct A;
struct A



вторая конструкция не является объявлением.


Спасибо: 0 
Цитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:07. Заголовок: 7 пишет: Объявление..


7 пишет:

 цитата:
Объявление функции 'void f( struct A * );' _может_ приветсти в объявлению типа 'A' в namespace, содержащем эту функцию. Поэтому вот этот код является well-formed:

void f(struct A*);
void f(A*);

int main(){}

согласно 3.3.2/6. Но если в процессе выполнения name lookup для идентификатора в elaborated-type-specifier будет определено что объявление с таким именем уже присутствует, то такая конструкция _не_ приведет к объявлению нового типа, а будет ссылаться на существующее объявление (7.1.6.3/2, 3.4.4/2).

Сыроежка пишет:

цитата:
Есть разница между двумя объявлениями:

struct A;
struct A


вторая конструкция не является объявлением.



Честно признаюсь, я не понял, как это ваше сообщение связано с первым вашим сообщением. То есть что вы пытаетесь опровергнуть или подтвердить? И что было не правильно в моем исходном сообщении. Что касается ваших слов, что

 цитата:

struct A;
struct A


вторая конструкция не является объявлением.


то вы сами себе противоречите. Вы привели ссылку из стандарта, которая как раз говорит, что вторая конструкция является объявлением, если не было найдено ранее объявленного такого типа. То есть ваш пример void f(struct A*); опровергает ваши же слова. В этом примере уточненное имя struct A без точки с запятой является как раз объявлением, если ранее этот тип не был объявлен. И об этом говорится в стандарте.
То есть о чем говорит стандарт. Стандарт говорит, что в коде программы могут встречаться две конструкции: одна с точкой запятой, а другая без точки с запятой. И между этими двумя конструкциями существует разница.

Вот пример описания из нового стандарта ( я беру рабочий проект стандарта 2011 )

6 The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
— for a declaration of the form
class-key attribute-specifier-seqopt identifier ;
the identifier is declared to be a class-name in the scope that contains the declaration, otherwise
— for an elaborated-type-specifier of the form
class-key identifier

То есть в первом случае берется конструкция с точкой с запятой, во втором - без точки с запятой. В старом 2003 стандарте это еще более наглядно: там нет добавления элемента attribute-specifier для первой конструкции. Поэтому там приводятся конструкции

class-key identifier;
и
class-key identifier

Но в любом случае не ясен предмет вашего возражения. То есть с чем именно вы не согласны в моем исходном сообщении, которое, я допускаю, выглядет не совсем ясно. То есть не понятно, что вы оспариваете?





Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:09. Заголовок: Сыроежка пишет: Вы..


Сыроежка пишет:

 цитата:
Вы привели ссылку из стандарта, которая как раз говорит, что вторая конструкция является объявлением, если не было найдено ранее объявленного такого типа. То есть ваш пример void f(struct A*); опровергает ваши же слова.



Для вас очевидна разница между elaborated-type-specifier и безымянным _объявлением_ параметра в случае с 'void f(struct A*);' ?

Сыроежка пишет:

 цитата:
То есть о чем говорит стандарт. Стандарт говорит, что в коде программы могут встречаться две конструкции: одна с точкой запятой, а другая без точки с запятой. И между этими двумя конструкциями существует разница.



Правильно. Но это отностится к elaborated-type-specifier, а не к обявлениям.

Спасибо: 0 
Цитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:10. Заголовок: 7 пишет: Для вас оч..


7 пишет:

 цитата:
Для вас очевидна разница между elaborated-type-specifier и безымянным _объявлением_ параметра в случае с 'void f(struct A*);' ?



Нет, я разницы не вижу. В этой конструкции struct A является уточненым именем типа независимо от того, присуьтствует идентификатор параметра или нет. То есть нет никакой разницы между двумя объявлениями функции void f( struct A * ) и void f( struct A *p ). В обоих случаях присутствует уточненное имя, и в обоих случаях это может быть объявлением типа, если ранее в области видимости такой тип не был уже объявлен.
7 пишет:

 цитата:
Правильно. Но это отностится к elaborated-type-specifier, а не к обявлениям



А эту вашу фразу я вообще не понимаю. Не могли бы вы разъяснить, что вы имели в виду в этой фразе?

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:12. Заголовок: 7 пишет: Правильно...


7 пишет:

 цитата:
Правильно. Но это отностится к elaborated-type-specifier, а не к обявлениям.



Или вы счтитаете, если я вас правильно понимаю, что в конструкции

void f( struct A *p );

struct A е является уточненным именем и не вводит новый тип в область видимости?

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:13. Заголовок: Сыроежка пишет: Нет..


Сыроежка пишет:

 цитата:
Нет, я разницы не вижу.


Плохо.


 цитата:
В этой конструкции struct A является уточненым именем типа независимо от того, присуьтствует идентификатор параметра или нет. То есть нет никакой разницы между двумя объявлениями функции void f( struct A * ) и void f( struct A *p ).



Я не об этом. Именованный/неименованный неважно. В отрыве от контекста конструкция 'struct A' не является объявлением. Но, она может присутствовать, например в, в объявлении.
Как здесь, например:

struct A* f();

С точки зрения синтаксиса elaborated-type-specifier является 'всего лишь' спецификатором типа (type-specifier). Объявление же, в свою очередь, представляет собой более сложную конструкция.

Сыроежка пишет:

 цитата:
А эту вашу фразу я вообще не понимаю. Не могли бы вы разъяснить, что вы имели в виду в этой фразе?


см. выше.



Спасибо: 0 
Цитата Ответить



ссылка на сообщение  Отправлено: 28.07.12 15:14. Заголовок: 7 пишет: В отрыве о..


7 пишет:

 цитата:
В отрыве от контекста конструкция 'struct A' не является объявлением. Но, она может присутствовать, например в, в объявлении.
Как здесь, например:

struct A* f();

С точки зрения синтаксиса elaborated-type-specifier является 'всего лишь' спецификатором типа (type-specifier). Объявление же, в свою очередь, представляет собой более сложную конструкция.



Извините, но я не понимаю, что вы хотите сказать, а самое главное, предмет вашего оппонирования. Как я уже говорил, уточненное имя либо вводит новый тип, либо ссылается на уже объявленный тип. Поэтому ваша фраза о контексте не совсем ясна. Мы же говорим не о ситаксисе, он и так понятен, а о семантике уточненного имени и двух его синтаксических формах, которые используются. И я не понимаю вашей фразы "Объявление же, в свою очередь, представляет собой более сложную конструкция". Какую более сложную конструкцию?

На самом деле в вашем примере
struct A* f(); спецификатор struct A является объявлением типа struct A конечно при условии, что ранее тип struct A не был объявлен в области видимости.

Чтобы было понятно, то приведу простой пример. Обычно, когда одна структура ссылается на другую. структуру, которая определяется ниже в коде, то пишут так

struct A; 

struct B
{
B( A * );
};

struct A {};


Здесь в конструкторе B( A * ); ссылка на имя A корректно, так как мы заранее объявили структуру A с помощью уточненного имени struct A; в форме с точкой с запятой.

Но абсолютно то же самое можно сделать с использованием уточненного имени в форме без точки запятой. То есть вышеуказанный код можно переписать в виде

 
struct B
{
B( struct A * );
};

struct A {};


Здесь также в конструкторе B( struct A * ); корректна ссылка на имя типа A, так как одновременно с объявлением параметра конструктора мы объявили новый тип, а именно тип struct A, с помощью опять-таки уточненного имени struct A, но уже с использованием конструкции без точки запятой.

Оба этих фррагмента кода эквиввалентны и вводят в область видимости новый тип struct A с помощью уточненного имени, но только используются две разные конструкции или формы записи, котрые и приводятся в стандарте при описании уточненных имен, и выжимку из которого я ранее привел.

Добро пожаловать в раздел С/С++ данного форума.

Спасибо: 0 
ПрофильЦитата Ответить
Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 3
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет