Недавно наткнулся на проблему при заполнении последовательности указателей нулевыми значениями. std::fill сказал мне, что NULL ему не годится, поскольку int не приводится к типу X *. Тогда я решил, наконец, реализовать nullptr. Обратившись к коллективному сознанию, написал следующее:
Но тут же появились вопросы:
1. Зачем он noncopyable?
2. Зачем у него скрыт дефолтный конструктор?
(Какие варианты неправильного применения это помогает отловить? В C++09 nullptr ведь и копируется, и создается?)
3. (MSVC71) Как бы сделать, чтоб отладчик не заходил в эти операторы по F11? Может, есть какая-то прагма? А то F11 уже влезает в чересчур много мест: begin()/end(), iterator -> const_iterator, custom_cast, NullptrType -> X * и т. п., которые давно отлажены или, как в данном случае, не делают вообще ничего интересного;
4. Стоит ли #define NULL nullptr? В конце концов, программа вправе рассчитывать на #define NULL 0. Я сделал дефайн, что помогло выловить N-ное количество неуставных использований NULL и прояснить код, правильно расставив NULL и literal 0 — может, теперь дефайн следует убрать (и s/NULL/nullptr/g — ох и коммит будет )?
5. Есть вариант впихнуть туда член void* dummy, чтобы sizeof(nullptr) == sizeof(void *). Стоит ли?
Здравствуйте, Roman Odaisky, Вы писали:
RO>Недавно наткнулся на проблему при заполнении последовательности указателей нулевыми значениями. std::fill сказал мне, что NULL ему не годится, поскольку int не приводится к типу X *. Тогда я решил, наконец, реализовать nullptr. Обратившись к коллективному сознанию, написал следующее: RO>
RO>Но тут же появились вопросы: RO>1. Зачем он noncopyable?
а зачем ему быть копируемым если на всю прогу нужен только 1 экземпляр данного класса?
RO>2. Зачем у него скрыт дефолтный конструктор?
чтобы ни кто не создавал ненужных экземпляров... одного nullptr вполне достаточно -- все что требуется от данного экземпляра в нужный момент приводиться к нулевому указателю на любой тип (включая datamemberов любых классов)
RO>(Какие варианты неправильного применения это помогает отловить? В C++09 nullptr ведь и копируется, и создается?)
не то чтоб неправильного... просто незачем копировать данный объект -- и все это сделано чтобы избежать генерации _ненужного_ кода (без которого всегда можно обойтись)
RO>4. Стоит ли #define NULL nullptr? В конце концов, программа вправе рассчитывать на #define NULL 0. Я сделал дефайн, что помогло выловить N-ное количество неуставных использований NULL и прояснить код, правильно расставив NULL и literal 0 — может, теперь дефайн следует убрать (и s/NULL/nullptr/g — ох и коммит будет )?
мое ИМХО: я бы сделал вообще `#undef NULL` и все вхождения NULL позаменял бы на `0'! А в тех редких случаях когда int не приводится в pointer использовал бы nullptr...
RO>5. Есть вариант впихнуть туда член void* dummy, чтобы sizeof(nullptr) == sizeof(void *). Стоит ли?
ну стоит конечно если у тебя в коде повсюду натыкано `sizeof(NULL)` что мне лична представляется малореалистичным
Здравствуйте, zaufi, Вы писали:
RO>>Недавно наткнулся на проблему при заполнении последовательности указателей нулевыми значениями. std::fill сказал мне, что NULL ему не годится, поскольку int не приводится к типу X *. Тогда я решил, наконец, реализовать nullptr.
Недавно оборачивал некоторые функции Win32 API. Шаблоны мне сказали, что int к HWND (который есть struct HWND__*) приводить категорически не будут. Тогда я сказал, что у типа HWND есть конструктор по умолчанию и что в качестве NULL буду использовать HWND().
Если соответствующего typedef’а для X* нет, то не сработает, конечно.
Здравствуйте, zaufi, Вы писали:
А>>а не проще было написать std::fill(..., ..., static_cast<X*>(0))?
Z>`std::fill(..., ..., nullptr)' выглядит красивее/эстетичнее/читабельнее
Z>правда код делается менее эффекивным при выключенном оптимизаторе... но в релизе все буит ОК
VC 7.1 в релизе, как для static_cast<X *>(0), так и для nullptr, генерит код вроде
mov DWORD PTR [eax], 0
add eax, 4
cmp eax, ecx
jne опять
так что с этим нормально. Даже лучше, чем memset — не тратим время на выравнивание. Разве что не догадывается анроллить...
[]
RO>Но тут же появились вопросы: RO>1. Зачем он noncopyable? RO>2. Зачем у него скрыт дефолтный конструктор? RO>(Какие варианты неправильного применения это помогает отловить? В C++09 nullptr ведь и копируется, и создается?)
действительно, зачем?
RO>3. (MSVC71) Как бы сделать, чтоб отладчик не заходил в эти операторы по F11? Может, есть какая-то прагма? А то F11 уже влезает в чересчур много мест: begin()/end(), iterator -> const_iterator, custom_cast, NullptrType -> X * и т. п., которые давно отлажены или, как в данном случае, не делают вообще ничего интересного;
хз )
RO>4. Стоит ли #define NULL nullptr? В конце концов, программа вправе рассчитывать на #define NULL 0. Я сделал дефайн, что помогло выловить N-ное количество неуставных использований NULL и прояснить код, правильно расставив NULL и literal 0 — может, теперь дефайн следует убрать (и s/NULL/nullptr/g — ох и коммит будет )?
думаю, не стоит. Просто можно постепенно переходить на его использование
RO>5. Есть вариант впихнуть туда член void* dummy, чтобы sizeof(nullptr) == sizeof(void *). Стоит ли?
Здравствуйте, zaufi, Вы писали:
RO>>(Какие варианты неправильного применения это помогает отловить? В C++09 nullptr ведь и копируется, и создается?) Z>не то чтоб неправильного... просто незачем копировать данный объект -- и все это сделано чтобы избежать генерации _ненужного_ кода (без которого всегда можно обойтись)
Ладно, пусть будет с закрытыми конструкторами. Если где в шаблонах лишнее копирование, будем считать логической ошибкой.
RO>>4. Стоит ли #define NULL nullptr? В конце концов, программа вправе рассчитывать на #define NULL 0. Я сделал дефайн, что помогло выловить N-ное количество неуставных использований NULL и прояснить код, правильно расставив NULL и literal 0 — может, теперь дефайн следует убрать (и s/NULL/nullptr/g — ох и коммит будет )?
Z>мое ИМХО: я бы сделал вообще `#undef NULL` и все вхождения NULL позаменял бы на `0'! А в тех редких случаях когда int не приводится в pointer использовал бы nullptr...
Ну это однозначно зря. Сравни:
char* s = real2str(0, 0, 0);
. . .
delete [] s;
и
char* s = real2str(0., NULL, 0u); // или nullptr
. . .
delete [] s;
RO>>5. Есть вариант впихнуть туда член void* dummy, чтобы sizeof(nullptr) == sizeof(void *). Стоит ли? Z>ну стоит конечно если у тебя в коде повсюду натыкано `sizeof(NULL)` что мне лична представляется малореалистичным
А sizeof(NULL) может != sizeof(void *) Я опять же размышляю о forward compatibility с C++09...
Здравствуйте, Roman Odaisky, Вы писали:
RO>>>4. Стоит ли #define NULL nullptr? В конце концов, программа вправе рассчитывать на #define NULL 0. Я сделал дефайн, что помогло выловить N-ное количество неуставных использований NULL и прояснить код, правильно расставив NULL и literal 0 — может, теперь дефайн следует убрать (и s/NULL/nullptr/g — ох и коммит будет )?
Z>>мое ИМХО: я бы сделал вообще `#undef NULL` и все вхождения NULL позаменял бы на `0'! А в тех редких случаях когда int не приводится в pointer использовал бы nullptr...
RO>Ну это однозначно зря. Сравни:
ну не так уж однозначно
я лична не испытываю необходимости в NULL вообще и по совету Б.Страуструпа использую 0 везде вместа него. Отчасти ввиду того что указателей в моем коде не так уж много (стараюсь сводить их к минимуму).
Здравствуйте, rusted, Вы писали:
А>>>а не проще было написать std::fill(..., ..., static_cast<X*>(0))?
Z>>`std::fill(..., ..., nullptr)' выглядит красивее/эстетичнее/читабельнее
R>если пишешь только для себя — то может и читабельнее, а вот кому-то другому видящему такой код придется еще смотреть что это за nullptr за такой.
Имя у него говорит само за себя, кроме того, nullptr есть в C++/CLI и в C++09 (будет). Ты еще скажи, что тот другой полезет узнавать, что делает std::fill.
Здравствуйте, Roman Odaisky, Вы писали:
RO>Здравствуйте, zaufi, Вы писали:
RO>>>1. Зачем он noncopyable? Z>>а зачем ему быть копируемым если на всю прогу нужен только 1 экземпляр данного класса?
RO>Вот только что оказалось, что копирование ему очень даже полезно. RO>
RO>3. (MSVC71) Как бы сделать, чтоб отладчик не заходил в эти операторы по F11? Может, есть какая-то прагма? А то F11 уже влезает в чересчур много мест: begin()/end(), iterator -> const_iterator, custom_cast, NullptrType -> X * и т. п., которые давно отлажены или, как в данном случае, не делают вообще ничего интересного;