Какой идиоматический способ документировать static_cast?

Я понимаю, что (MyType)foo, MyType(foo) и static_cast<MyType>(foo) в чем-то похожи в том, что первые два становятся последними (РЕДАКТИРОВАТЬ: я был исправлен. Предыдущее предложение неверно.) Мой вопрос: какую идиоматическую версию использовать? Учитывается ли контекст?

Поскольку я знаю, что приведение типов в стиле C не является предпочтительным в C ++, это сводится к функциональным приведениям и static_cast.


person griotspeak    schedule 10.12.2013    source источник
comment
stackoverflow .com / questions / 103512 /.   -  person aardvarkk    schedule 10.12.2013
comment
Нет, это не то же самое. Например, первый становится reinterpret_cast, а не static_cast.   -  person Sergey Kalinichenko    schedule 10.12.2013
comment
Тот факт, что вы можете использовать grep static_cast, делает его в моих глазах победителем.   -  person molbdnilo    schedule 10.12.2013
comment
Я смущен. Разве MyType (foo) не вызывает конструктор?   -  person Bathsheba    schedule 10.12.2013
comment
Я бы сказал, что в C ++ идиоматично использовать правильное приведение для ситуации. Насколько я знаю, это всегда должно быть одно из static, reinterpret, dynamic или const. Но никогда не актерский состав в функциональном стиле или в стиле C.   -  person aardvarkk    schedule 10.12.2013
comment
@Bathsheba: Это так. Остальные два тоже.   -  person Nawaz    schedule 10.12.2013
comment
Интересный. Я не понимал, что приведение в стиле C может привести к любому количеству приведений. Спасибо за разъяснение.   -  person griotspeak    schedule 10.12.2013


Ответы (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 из типа указателя.

person Steve Jessop    schedule 10.12.2013

В моей книге использование явного приведения является идиоматическим.

static_cast<MyType>(foo) лучше, чем (MyType)foo, потому что он более выразительный и ясный. Это особенно полезно во многих случаях, когда (MyType)foo не означает то, что вы ожидаете. Например, в ваших примерах эти случаи могут вообще не уменьшаться до static_cast, а до reinterpret_cast.

При обзоре кода я бы отверг все без исключения приведения в стиле C к неарифметическим типам.

person John Dibling    schedule 10.12.2013
comment
Я бы допустил их для арифметических типов. Я не думаю, что std::tolower(static_cast<unsigned char>(c)); лучше, чем std::tolower((unsigned char)c);. Типа назначения достаточно, чтобы убедиться, что он не может удалить const или что-нибудь еще страшное. Но если вам нужно очень простое правило, то нужно запретить их. IIRC также есть некоторые глупые вещи, которые могут быть выполнены только с использованием актеров в стиле C, но которые почти во всех обстоятельствах вам все равно нечего делать. Приведение указателя на указатель на частную базу или что-то в этом роде. - person Steve Jessop; 10.12.2013
comment
@SteveJessop: На самом деле я не думал об арифметических типах. Полагаю, я более или менее согласен с вами. Меньше в том смысле, что тип i может быть изменен в будущем, но это кажется немного фантастическим. - person John Dibling; 10.12.2013
comment
@SteveJessop: Верно. - person John Dibling; 10.12.2013
comment
извините, удалил комментарий из-под вас. У меня возникла внезапная паника, что произойдет, если он изменится на тип указателя (вы бы хотели, чтобы код не компилировался, поскольку, хотя вызов tolower безопасен, он бессмысленен), но у меня нет времени проверить прямо сейчас: -) - person Steve Jessop; 10.12.2013
comment
@SteveJessop: Еще раз верно, но, как я уже сказал ранее, это кажется немного фантастическим. FWIW, я использую явные приведения для арифметических типов, но я не отвергаю их приведения в стиле C в обзоре кода. Я могу начать делать последнее. Полагаю, лучше всегда в безопасности, чем очень редко извиняться. - person John Dibling; 10.12.2013

Во-первых, приведения функциональных стилей являются новыми приведениями в стиле 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 это чрезмерное упрощение, но достаточно близкое к истине.

person Yakk - Adam Nevraumont    schedule 10.12.2013
comment
в C ++ нет явного безопасного приведения - хм. Функция шаблона, которая выполняет static_cast и отключена, если оба параметра шаблона являются типами указателей и (после remove_pointer) источник является базой назначения? - person Steve Jessop; 10.12.2013
comment
@SteveJessop приведение указателя вверх также безопасно, так что да. И СФИНАЭ, наверное, сработает. Это касается каждого случая? - person Yakk - Adam Nevraumont; 10.12.2013
comment
Кстати, поиск в Интернете по запросу «C ++ explicit_cast» привел меня туда: stackoverflow.com/questions/5693432/making- auto-cast-safe (короче говоря, были вызваны унифицированная инициализация C ++ 11 и std::is_constructible) - person gx_; 10.12.2013