C++, зогатка.
http://msdn.microsoft.com/en-us/library/9zkz8dx6%28v=VS.100%...
По-моему, гонит! Какой такой дефолтный конструктор должен быть у константного указателя на функцию, который внезапно есть у неконстантных, а тут — нет?
vs2010 несколько более многословна: По-моему, гонит! Какой такой дефолтный конструктор должен быть у константного указателя на функцию, который внезапно есть у неконстантных, а тут — нет?
template <int table_size>
int compare_messages(const int (&comp_table)[table_size], ...)
То передача по ссылке обязательна, иначе оказывается компилиться. Впрочем, отлично компилится если указать размер ручками, на call site, в смысле, явно инстанцировать темплейт. И массив передаётся по ссылке, а не по значению!
Видимо, дело в том, что "int x[n]" типом не является (как бы порой не казалось обратное потому что совместимость с С, а вот "int (&x)[n]" — полноценный тип.
Плюсы такие няшные!
#include <stdio.h>я в плюсах не гульбарий, сначала не обратил внимания.
typedef int (*f_ptrconst int x, const int y);
// error: uninitialized const member `<anonymous struct>::f'
//struct {
// const char * s;
// const f_ptr f;
//} test00 = { };
по сишному - всё ч0тко.
Но меня дико удивляет, почему простой указатель на функцию отказывается дефолт-инициализироваться (если я правильно понимаю, гарантированно в NULL). Причём только если он ещё и const.Замени f_ptr на int, у тебя будет то же самое. Разница в том, куда применяется const: поле типа const char* можно перезаписать, а const int или const f_ptr - нельзя.
Так const относится к char, а не к *. Если сделать char* const, то нельзя будет переписать.
Ок, понял. Ты прав.
typedef char * cptr;
struct {
const cptr c1;
const char * c2;
} test00 = { };
Одно из них выдаёт ошибку, другое — нет, естественно.
Это даже логично, если подумать. Типа, если развернуть тайпдеф и явно указать приоритеты операций, то получится как бы это:
const cptr c1; <==> (const (char * c;
const char * c1; <==> const char) *) c;
Что не является валидным кодом, кстати говоря, нельзя скобочки в декларации-то. Поэтому корректно разворачивается тайпдеф через жопу: "char * const c;"
ок, спасибо, я кажется внезапно намного лучше стал понимать, как работает const!
Кстати интересно, а чё в С-то работает? То есть, почему в C++ внезапно запретили инициализировать константные поля структур вот так вот?
Так const относится к char, а не к *. Если сделать char* const, то нельзя будет переписать.На этом и основывался мой ответ.
Тред не читал, но
В C смысл (семантика) const иная. Что-то типа "эта ячейка находится в ПЗУ".
про const не прав.
Разница в том, куда применяется const: поле типа const char* можно перезаписать, а const int или const f_ptr - нельзя.Подожди, стоп, я внезапно понял, что вопрос-то не только в этом.
Почему компилятор волнует, что там можно перезаписать, а что — нет?
Инициализирует-то он всё совершенно нормально. Причём неконстантные поля для которых не указан инициализатор, замечательно инициализирует дефолтными значениями, (по сишному стандарту, сквозь дебри плюсового я что-то не в состоянии продираться, но там то же самое очевидно).
Проблема не в том, что ему не хочется инициализировать константные поля (как мне внезапно вчера показалось проблема в том, что ему почему-то не хочется инициализировать константные поля дефолтными значениями.
Почему? Какому-то мудаку из комитета показалось, что я не должен этого хотеть, и он потребовал сделать это даже не варнингом, а еггогом?
Почему? Какому-то мудаку из комитета показалось, что я не должен этого хотеть, и он потребовал сделать это даже не варнингом, а еггогом?В Си++ поля классов и структур не инициализируются никакими дефолтными значениями. Забыл, для чего это сделано, для скорости что ли...
Забыл, для чего это сделано, для скорости что ли...для скорости плюсам надо пендаля отвесить.
В тред приглашается Thinking in C++:
The const was taken from C++ and incorporated into Standard C, albeit quite differently. In C, the compiler treats a const just like a variable that has a special tag attached that says “Don’t change me.” When you define a const in C, the compiler creates storage for it, so if you define more than one const with the same name in two different files (or put the definition in a header file the linker will generate error messages about conflicts. The intended use of const in C is quite different from its intended use in C++ (in short, it’s nicer in C++).
Не будет компилятор C менять ячейку const, так что её смело можно разместить в ROM. А вот C++ - может (надо const volatile, чтобы не менял).
Смотри, в С фишка такая. Если ты просто заводишь локальную переменную какого-то типа, то она не инициализируется (а глобальные переменные инициализируются всегда, кстати).
Однако как только ты её принудительно инициализируешь, хоть как "struct somestruct = {};", все поля и подполя этой структуры рекурсивно инициализируются дефолтным значением. Нулём, как правило. Причём в C99 ваще дико мощные шняжки есть, вот смотри http://pastebin.com/1aRcCz3E
Аналогично для инициализированных массивов, инициализированнных структур с массивами внутри — всё инициализируется либо указанными тобой значениями, либо дефолтными.
Прочитай, короче, 7.8 C99 standard, это три странички определений, плюс ещё две странички незамутнённого щастья в виде примеров, типа:
struct { int a[3], b; } w[] =
{ [0].a = {1}, [1].a[0] = 2 };
Чего делать нельзя — это указывать в самих структурах какие-то недефолтные дефолтные значения, типа "struct s { int a = 1; int b};".
Соответственно, в плюсах правила инициализации для POD типов обязаны быть такими же — если есть инициализатор, то указанные элементы инициализируются указанными значениями, всё остальное — эквивалентами нуля.
Совершенно непонятно, с чего бы вдруг кто-то решил запретить эту дефолтную инициализацию нулём для константных полей. У меня есть два варианта — либо это как-то дико конфликтует с какой-то другой фишкой плюсов (ну, там, не знаю, они на самом деле даже для POD генерят дефолтный конструктор, а там немножко другие требования либо какой-то мудак из стандартного комитета решил, что если я не указал инициализатор для константного поля, то это я наверное ошибся, и компилятор должен мне об этом ненавязчиво намекнуть. В последнем случае это дикое мудачество, ибо вот у меня совершенно нормальный пример — я хочу завести себе константный массивчег с описанием особой херни, и некоторые из полей в инициализаторе предпочёл бы не указывать, пусть остаются нуллами.
: и чо? Твоя цитата совершенно не проливает свет на смысл конст в плюсах, особенно в данном случае. И что С++ "может"? Ты опечатался, или имел в виду, что может поменять? Я, короче, не могу пропарсить твою последнюю фразу!
Не будет компилятор C менять ячейку const, так что её смело можно разместить в ROM.ORLY?
const int a = 0;
int *b = (void *)&a;
*b = 1;
Он может и будет разместить её в ROM, а на твоё высказывание ты словишь undefined behaviour, за присвоение пойнтеров несовместимого типа.
EDIT:
У меня проблемы со второй половиной утверждения Жюнита, чо в плюсах-то происходит?
5 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
мой претензия к части "не будет менять" а не к "может разместить в ром"
Слушьте, у меня башка гудит, хотелось бы вопросы покороче. Семантика const в плюсах ИМХО тривиальна, простите за снобизм. В сях const никогда не использовал, но запомнил вышеприведённый отрывок.
да, семантика такая, но прострелить себе ноги никто не запрещает.
Ну, это же C++ - каждый имеет право идти в ад собственной дорогой.
Если это a будет статическим, то на винде должно случиться AV, если автоматическим, то MSVC даст поменять через указатель, но в оптимизированном коде вместо a будет подставляться константа в сгенеренном коде, поэтому через указатель будем получать измененное значение, а через a — из объявления. Кто-нибудь может и правда в «ROM» поместит и будет AV/segfault при таких штуках. Можно gcc потестить.
ну да, на g++ даже в -O0 аналогичное поведение: на статических segfault, автоматические дает менять, но константы подставляет прямо в код.
Так я это к чему, в чем отличие с C должно быть-то?
Семантика const в плюсах ИМХО тривиальна, простите за снобизм. В сях const никогда не использовал, но запомнил вышеприведённый отрывок.ЧТО-ТО ТЫ ПИЗДИШЬ.
Ок, я откопал C++2003 standard (он кстати есть в интернетах совершенно не драфт буквально первой же ссылкой!) и посмотрел в него. Раздел 7.1.5.1, интересные места:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
If an attempt is made to refer to an object defined with a volatile-qualified type through the use of an lvalue with a non-volatile-qualified type, the program behaviour is undefined.
[Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C + + as they are in C.]
Ну и где тут про то, что конст поля можно менять?
Единственное отличие от С, как я понимаю, вот в этом: An object declared in namespace scope with a const-qualified type has internal linkage unless it is explicitly declared extern or unless it was previously declared to have external linkage.
В С этого нет, и внимательный взгляд на твою цитату из Thinking C++ вызывает жуткие подозрения, что автор сделал СОВЕРШЕННО неверные выводы о природе конст в си и плюсах на основе эффектов, производимых этим немудрящим отличием. Что де в плюсах под конст не выделяется память или ещё что-то. Вот в C# это так, кстати. Ну или может быть тебе удалось выдрать цитату дичайше из конекста.
Обесни, короче, "тривиальную семантику" конст в плюсах, как ты её понимаешь. И чем она отличается от сишной, на основе отрывка.
edit: проверил, кстати, в сях объявление "const int x;" замечательно компилируется, x инициализируется в 0. Если объявить "extern const int x;" то ругается линкер, пока не объявишь "[const] int x = 17;" в другом файле. И печатается 17, кстати.
в плюсах на "const int x;" ругается, "extern const int x;" жрёт, линкер требует объявить x в другом файле либо без const, либо как "extern const int x = 23;", либо даже как "extern const int x; const int x = 23;"
То есть типа да, единственное отличие, но может ввести в заблуждение неподготовленного человека!
Так это, а по моему вопросу, есть у кого-нибудь идеи, почему плюсы не позволяют автоматическую инициализацию конст полей POD структур?
не оно ли?
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or
array thereof the object shall be default-initialized; if the object is of const-qualified type, the underlying
class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a nonstatic
object, the object and its subobjects, if any, have an indeterminate initial value); if the object or any
of its subobjects are of const-qualified type, the program is ill-formed.
Кстати, все const-qualified members класса обязаны присутствовать в списке инициализации конструктора. Если отсутствует - "the program is ill-formed". Это 12.6.2, параграф 4.
Смотри в чём фишка: 12.6.2 раздел посвящён инициализации конструкторами. Там из соображений производительности неинициализированные поля оказываются undefined. При этом разумеется любое неинициализированное const поле — это стопроцентный баг, потому что его никак нельзя использовать.
Тем не менее сам же 12.6.1:2 сразу ссылается на 8.5:
When an aggregate (whether class or array) contains members of class type and is initialized by a brace-enclosed initializer-list (8.5.1 each such member is copy-initialized (see 8.5) by the corresponding assignment-expression. If there are fewer initializers in the initializer-list than members of the aggregate, each member not explicitly initialized shall be value-initialized (8.5).
В таком случае ругаться на отсутвие явной инициализации конст полей неправильно, ибо там нет никакого undefined behavior, они гарантированно инициализируются нулями соответствующих типов и их можно после этого замечательно читать.
Там даже есть такое сильное утверждение: 8.5.1:8
An empty initializer-list can be used to initialize any aggregate. If the aggregate is not an empty class, then each member of the aggregate shall be initialized with a value of the form T (5.2.3 where T represents the type of the uninitialized member.
(потом правда идут уточнения, что референсы всё-таки так инициализировать нельзя, но про const вроде ничего нет).
Так что либо я что-то не понимаю, либо авторы gcc и MSVC неправильно интерпретировали стандарт.
Есть ли тут у кого-нибудь ещё какие-нибудь компиляторы плюсов под рукой?
Есть ли тут у кого-нибудь ещё какие-нибудь компиляторы плюсов под рукой?есть, и много.
Sun Studio 12u2 на Solaris и на x86_64, и на SPARC,
HP aCC на HP-UX и на Itanium2, и на PA-RISC (под PA-RISC правда версия сейчас стоит древняя как говно мамонта)
IBM xlC на AIX
update:
раскомментировал структуру test00 и вот что вышло:
1) Sun Studio 12u2 на SPARC: скомпилил без предупреждений (были включены все предупреждения)
# CC -V
CC: Sun C++ 5.11 SunOS_sparc 2010/08/13
2) HP aCC на Itanium2 скомпилил выдав предупреждение, причём не только на test00, но и на test1.
# aCC -V
aCC: HP C/aC++ B3910B A.06.24 [Dec 04, 2009]
# aCC -g -O0 -AA vj.cpp
"vj.cpp", line 6: warning #2368-D: class "<unnamed>" defines no constructor to
initialize the following:
const member "<unnamed>::f"
struct {
^
"vj.cpp", line 16: warning #2368-D: class "<unnamed>" defines no constructor
to initialize the following:
const member "<unnamed>::f"
struct {
^
3) IBM xlC на AIX - скомпилил без предупреждений
$ xlC -qversion
IBM XL C/C++ Enterprise Edition for AIX, V9.0
Version: 09.00.0000.0002
Если там где-нибудь всё ок, то можно будет залезть в мсфт.коннект и к гццшникам и почморить их! =)
Да, компили типа простейшую версию,
int main(int argc, char ** argv)
{
struct { const int x; } s = {};
return s.x;
}
1) Sun Studio на SPARC - скомпилировал без предупреждения (если не считать argc/argv is defined but not used поле проинициализировано нулём,
2) HP aCC на HP-UX/Itanium2 - скомпилировал с предупреждением, но тоже всё работает, проинициализировано нулём.
"vj2.cpp", line 3: warning #2368-D: class "<unnamed>" defines no constructor
to initialize the following:
const member "<unnamed>::x"
struct { const int x; } s = {};
^
3) IBM xlC - ОК, скомпилил без предупреждений, проинициализировано нулём
Проверял в gcc 3.4.6, 4.2.2.на всякий случай проверил на последнем gcc что у меня есть - 4.4.0 в Centos 5 - по-прежнему ошибка компиляции.
$ g++44 -Wall vj2.cpp
vj2.cpp: In function ‘int main(int, char**)’:
vj2.cpp:3: error: uninitialized const member ‘main(int, char**)::<anonymous struct>::x’
Оставить комментарий
bleyman
Суть зогатки: меня не очень удивляет, что указатели на функции можно бесконечно дереференсить и они по-прежнему остаются как бы функциями.
Но меня дико удивляет, почему простой указатель на функцию отказывается дефолт-инициализироваться (если я правильно понимаю, гарантированно в NULL). Причём только если он ещё и const.
Проверял в gcc 3.4.6, 4.2.2.