Какой идиоматический способ документировать static_cast?
Я это понимаю (MyType)foo
, MyType(foo)
, а также static_cast<MyType>(foo)
несколько похожи в том, что первые два становятся последними (РЕДАКТИРОВАТЬ: я был исправлен. Предыдущее предложение не соответствует действительности.) Мой вопрос: какую идиоматическую версию использовать? Является ли контекст соображением?
Поскольку я знаю, что приведение типов в C не является предпочтительным в C++, оно сводится к функциональным приведениям и static_cast.
3 ответа
Первые два, (MyType)foo
а также MyType(foo)
, имеют такое же значение. Что это означает, зависит от MyType
и тип foo
, Так, например:
((int)1.0); // a static_cast
int i = 0;
((char*)&i); // a reinterpret_cast
const int j = 0;
((int*)&j); // a const_cast
((char*)&j); // a combination of a const_cast and a reinterpret_cast
typedef char *CharPtr;
CharPtr(&j); // a C-style cast, same as ((CharPtr)&j) or ((char*)&j)
Поэтому причина, по которой броски в стиле C не являются предпочтительными, заключается в том, что приведения в стиле C делают много разных вещей. C++ предоставляет средства для различия между ними. Во многих случаях это облегчает понимание кода, особенно когда вы пишете универсальный код на C++, а типы менее очевидны, чем обычно на C.
Вот почему static_cast
и т. д. в C++ предпочтительнее - они явно предназначены для читателя, что в противном случае им было бы трудно решить самим. static_cast
Кстати, он также делает несколько разных вещей, так что в принципе вам может потребоваться еще больше разыгрываний. Но static_cast
по крайней мере, делает меньше, чем в стиле C.
Приведения в функциональном стиле с одним аргументом - в C-стиле. Они четко определены в стандарте и означают одно и то же во всех случаях.
Теперь в некоторых случаях вы можете принять приведение в стиле C, когда оно написано в функциональном синтаксисе, но не в том случае, если оно написано с использованием синтаксиса C. Обычно это происходит, когда типом назначения является класс, и очевидно, какой конструктор будет вызываться. В таком случае, std::vector<int>(0)
короче чем static_cast<std::vector<int>>(0)
и, как правило, понятнее, поскольку его можно прочитать как вызов конструктора. Но он по-прежнему та же "опасность", что и письмо (std::vector<int>)0
, Если бы вы писали каждое преобразование таким образом, то в итоге вы бы случайно удалили const
из типа указателя.
Использование явного приведения является идиоматическим в моей книге.
static_cast<MyType>(foo)
лучше, чем (MyType)foo
потому что это более выразительно и явно. Это особенно полезно во многих случаях, когда (MyType)foo
не означает, что вы ожидаете, что это будет означать. Например, в ваших примерах эти случаи могут не сводиться к static_cast
вообще, но reinterpret_cast
,
В обзоре кода я бы отверг все и все приведения в стиле C к неарифметическим типам.
Во-первых, будучи функциональными стилями, новичками в C++ являются преобразования в стиле C. Они делают то же самое.
C-стиль бросает сначала попытаться static_cast
и если это не сработает, делает reinterpret_cast
1 Это плохо, потому что операции, предназначенные для простого изменения, могут стать очень странными.
Например, предположим, у вас есть Foo* f
и совершенно не связанный класс Bar
, Вы можете (Bar*)f
и это будет молча переосмыслить *f
быть Bar
вероятно ведет к неопределенному поведению.
static_cast
будет работать только в этой ситуации, если Foo
а также Bar
были родственные типы.
Теперь даже static_cast
может быть слишком сильным, поэтому я часто пишу implicit_cast
или же non_cast
Шаблон, который конвертирует без приведения. Точно так же я пишу as_const
это добавляет const
вместо того, чтобы требовать const_cast
(который может как добавлять, так и удалять const
и удаление const
гораздо опаснее, чем добавить его).
Несколько досадно, что в C++ нет явного безопасного приведения, и я не знаю, как его написать (тот, который будет выполняться explicit
конструкторы, но не будут "сбрасывать" иерархию типов, как static_cast
будут).
1 это слишком упрощенное, но достаточно близкое к истине.