Как правильно использовать:
static_castdynamic_castconst_castreinterpret_cast(type)valuetype(value)Как решить, что использовать в конкретных случаях?
Для некоторых полезных конкретных примеров использования различных типов приведений вы можете проверить первый ответ на аналогичный вопрос в эта другая тема.
Вы можете найти действительно хорошие ответы на свой вопрос выше. Но я хотел бы поставить здесь еще один момент: @ e.James: «Нет ничего, что могли бы сделать эти новые операторы преобразования в C++, а преобразование в стиле C не могут.
@BreakBadSP Новые преобразования имеют тип нет только для лучшей читаемости кода. Они созданы для того, чтобы усложнить выполнение опасных вещей, таких как отбрасывание констант или приведение указателей вместо их значений. static_cast имеет гораздо меньше возможностей сделать что-то опасное, чем приведение в стиле c!
@FourtyTwo согласился





это отвечает на ваш вопрос?
Я никогда не использовал reinterpret_cast и задаюсь вопросом, не пахнет ли чехол, который в нем нуждается, плохим дизайном. В кодовой базе, которую я работаю, много используется dynamic_cast. Разница с static_cast заключается в том, что dynamic_cast выполняет проверку во время выполнения, которая может (безопаснее) или не может (больше накладных расходов) быть тем, что вы хотите (см. msdn).
Я использовал reinterpret_cast с одной целью - получить биты из двойника (того же размера, что и на моей платформе).
reinterpret_cast требуется, например для работы с COM-объектами. CoCreateInstance () имеет выходной параметр типа void ** (последний параметр), в котором вы передадите свой указатель, объявленный, например, как «INetFwPolicy2 * pNetFwPolicy2». Для этого вам нужно написать что-то вроде reinterpret_cast <void **> (& pNetFwPolicy2).
Возможно, есть другой подход, но я использую reinterpret_cast для извлечения фрагментов данных из массива. Например, если у меня есть char*, содержащий большой буфер, полный упакованных двоичных данных, которые мне нужно пройти и получить отдельные примитивы разных типов. Примерно так: template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
Я никогда не использовал reinterpret_cast, его не так много применений.
Лично я видел использование reinterpret_cast только по одной причине. Я видел необработанные данные объекта, хранящиеся в базе данных типа данных «blob», а затем, когда данные извлекаются из базы данных, reinterpret_cast используется для преобразования этих необработанных данных в объект.
Отсутствие комментариев к reinterpret_cast говорит о многом. Если ваш C++ использует другую библиотеку C, потом, вы увидите много reinterpret_cast.
Используйте dynamic_cast для преобразования указателей / ссылок в иерархии наследования.
Используйте static_cast для преобразования обычных типов.
Используйте reinterpret_cast для низкоуровневой переинтерпретации битовых шаблонов. Используйте с особой осторожностью.
Используйте const_cast для отбрасывания const/volatile. Избегайте этого, если вы не застряли в использовании API, неверного по константе.
Будьте осторожны с dynamic_cast. Он полагается на RTTI, и это не будет работать должным образом за пределами общих библиотек. Просто потому, что вы создаете исполняемые и разделяемые библиотеки независимо друг от друга, не существует стандартизованного способа синхронизации RTTI между разными сборками. По этой причине в библиотеке Qt существует qobject_cast <>, который использует информацию о типе QObject для проверки типов.
static_cast - это первое приведение, которое вы должны попытаться использовать. Он выполняет такие вещи, как неявные преобразования между типами (например, int в float или указатель на void*), а также может вызывать функции явного преобразования (или неявные). Во многих случаях явно указывать static_cast не обязательно, но важно отметить, что синтаксис T(something) эквивалентен (T)something, и его следует избегать (подробнее об этом позже). Однако T(something, something_else) безопасен и гарантированно вызывает конструктор.
static_cast также может проходить через иерархии наследования. В этом нет необходимости при приведении вверх (к базовому классу), но при приведении вниз его можно использовать, если оно не выполняется через наследование virtual. Однако он не выполняет проверку, и это неопределенное поведение для static_cast вниз по иерархии до типа, который на самом деле не является типом объекта.
const_cast может использоваться для удаления или добавления const к переменной; никакое другое приведение C++ не способно удалить его (даже reinterpret_cast). Важно отметить, что изменение ранее существовавшего значения const не определено только в том случае, если исходной переменной является const; если вы используете его, чтобы убрать ссылку на const на то, что не было объявлено с const, это безопасно. Это может быть полезно, например, при перегрузке функций-членов на основе const. Его также можно использовать для добавления const к объекту, например для вызова перегрузки функции-члена.
const_cast также работает аналогично с volatile, хотя это встречается реже.
dynamic_cast используется исключительно для обработки полиморфизма. Вы можете привести указатель или ссылку на любой полиморфный тип к любому другому типу класса (полиморфный тип имеет по крайней мере одну виртуальную функцию, объявленную или унаследованную). Вы можете использовать его не только для заброса вниз - вы можете забрасывать его боком или даже вверх по другой цепи. dynamic_cast найдет желаемый объект и, если возможно, вернет его. Если не может, он вернет nullptr в случае указателя или выбросит std::bad_cast в случае ссылки.
Однако dynamic_cast имеет некоторые ограничения. Это не работает, если в иерархии наследования есть несколько объектов одного типа (так называемый «ужасный ромб») и вы не используете наследование virtual. Он также может пройти только через публичное наследование - он всегда не сможет пройти через наследование protected или private. Однако это редко является проблемой, поскольку такие формы наследования редки.
reinterpret_cast - наиболее опасное приведение, и его следует использовать очень экономно. Он превращает один тип напрямую в другой - например, преобразование значения из одного указателя в другой, или сохранение указателя в int, или множество других неприятных вещей. По большому счету, единственная гарантия, которую вы получаете с reinterpret_cast, заключается в том, что обычно, если вы возвращаете результат обратно к исходному типу, вы получите точно такое же значение (но нет, если промежуточный тип меньше исходного типа). reinterpret_cast также не может выполнять ряд преобразований. Он используется в основном для особенно странных преобразований и битовых манипуляций, таких как преобразование потока необработанных данных в фактические данные или хранение данных в младших битах указателя на выровненные данные.
C-стиль гипс и приведение в функциональном стиле представляют собой приведения с использованием (type)object или type(object), соответственно, и функционально эквивалентны. Они определяются как первые из следующих успешных:
const_caststatic_cast (правда, игнорируя ограничения доступа)static_cast (см. Выше), затем const_castreinterpret_castreinterpret_cast, затем const_castПоэтому в некоторых случаях его можно использовать в качестве замены для других приведений, но это может быть чрезвычайно опасно из-за способности превращаться в reinterpret_cast, и последнее следует предпочесть, когда требуется явное приведение, если вы не уверены, что static_cast будет успешным или reinterpret_cast выйдет из строя. Даже в этом случае рассмотрите более длинный и явный вариант.
Приведения в стиле C также игнорируют управление доступом при выполнении static_cast, что означает, что они имеют возможность выполнять операцию, которую не может выполнить ни одно другое приведение. Тем не менее, это в основном кладж, и, на мой взгляд, это еще одна причина избегать приведений в стиле C.
Я бы порекомендовал в качестве первого варианта dynamic_cast <>, который, конечно, работает только так, как описано выше. Всякий раз, когда вы можете выполнить dynamic_cast <>, он будет проверять, действительно ли тип соответствует вашему мнению.
dynamic_cast предназначен только для полиморфных типов. вам нужно использовать его только при преобразовании в производный класс. static_cast - это, безусловно, первый вариант, если вам специально не нужна функциональность dynamic_cast. Это не какая-то чудодейственная серебряная пуля "типографского литья" вообще.
В разделе reinterpret_cast показано нарушение псевдонима и, следовательно, неопределенное поведение. Если вам нужно такое преобразование типов, лучше всего это сделать с помощью memcpy.
Отличный ответ! Одно быстрое замечание: static_cast может потребоваться для преобразования иерархии в случае, если у вас есть Derived * & для преобразования в Base * &, поскольку двойные указатели / ссылки не создают автоматически иерархию. С такой (честно говоря, не типичной) ситуацией я столкнулся две минуты назад. ;-)
Определяется ли поведение использования const_cast для указателя? Скажем, у меня есть std::string s("hi"); auto p = s.c_str();, а p - это const char*. Выполняет ли const_cast<char*>(p) = "hello"; определенное поведение?
в главе 22 «Язык программирования C++» более подробно описаны const_cast и dynamic_cast. @ unj2
@ unj2 Аннотации C++: icce.rug.nl/documents/cplusplus/cplusplus03.html#an241
* "никакое другое преобразование C++ не способно удалить const (даже reinterpret_cast)" ... правда? А как насчет reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
Я думаю, что важная деталь, отсутствующая выше, заключается в том, что dynamic_cast снижает производительность во время выполнения по сравнению со static или reinterpret_cast. Это важно, например, в программном обеспечении в реальном времени.
Возможно, стоит упомянуть, что reinterpret_cast часто является предпочтительным оружием при работе с набором непрозрачных типов данных API.
Думаю, следует отметить, что динамический состав работает только при включенном информация о типе времени выполнения. Кажется, это по умолчанию для всех основных компиляторов.
«Это, в основном, кладж, и, на мой взгляд, это еще одна причина, по которой следует избегать приведений в стиле C.» Похоже, вы говорите, что нет ситуаций, когда приведение в стиле C. Итак, что бы вы сказали о следующем примере: stackoverflow.com/q/38682050/2725810?
Почему dynamic_cast «почти исключительно» используется для полиморфизма? Есть ли ситуация, когда dynamic_cast подходит для чего-то другого, кроме полиморфизма?
Этот ответ не объясняет, что делает приведение в функциональном стиле. Это можно было добавить?
В дополнение к примечанию с информацией о типе времени выполнения, еще одно важное замечание: dynamic_cast правильно работает только с объектами с vtables. Другими словами, в производном классе должна быть хотя бы одна виртуальная функция.
@Mehrdad: эффект обратного преобразования к типу указателя разные определяется реализацией.
А как насчет std::any_cast? Может ли кто-нибудь объяснить разницу в методах, описанных здесь. Это только для объектов типа std::any?
static_cast может выполнять неявные преобразования, преобразование указателей с помощью void* и преобразование вверх / вниз в иерархии классов, обратите внимание на 3-й случай, он также хорошо работает с множественным наследованием, что означает, что он может смещать адрес при преобразовании между указателями на объект и подобъект
Я обнаружил удивительный (раздражающий) C++ характерная черта, т.е. downcast может происходить только через публичные наследования. Теперь я рассматриваю приведение в стиле C как возможное обходное решение.
(Выше было дано много теоретических и концептуальных объяснений)
Ниже приведены некоторые из практические примеры, когда я использовал static_cast, dynamic_cast, const_cast, reinterpret_cast.
(Также ссылается на это, чтобы понять объяснение: http://www.cplusplus.com/doc/tutorial/typecasting/)
static_cast:
OnEventData(void* pData)
{
......
// pData is a void* pData,
// EventData is a structure e.g.
// typedef struct _EventData {
// std::string id;
// std:: string remote_id;
// } EventData;
// On Some Situation a void pointer *pData
// has been static_casted as
// EventData* pointer
EventData *evtdata = static_cast<EventData*>(pData);
.....
}
dynamic_cast:
void DebugLog::OnMessage(Message *msg)
{
static DebugMsgData *debug;
static XYZMsgData *xyz;
if (debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
// debug message
}
else if (xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
// xyz message
}
else/* if ( ... )*/{
// ...
}
}
const_cast:
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
reinterpret_cast:
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
Теория некоторых других ответов хороша, но все же сбивает с толку: просмотр этих примеров после прочтения других ответов действительно делает их все понятными. То есть без примеров я все еще не был уверен, но с ними я теперь уверен в том, что означают другие ответы.
О последнем использовании reinterpret_cast: разве это не то же самое, что и использование static_cast<char*>(&val)?
@LorenzoBelli Конечно, нет. Вы пробовали? Последний не является допустимым C++ и блокирует компиляцию. static_cast работает только между типами с определенными преобразованиями, видимыми отношениями по наследству или в / из void *. Для всего остального есть другие составы. reinterpret cast для любого типа char * разрешено читать представление любого объекта - и это единственный случай, когда это ключевое слово полезно, а не безудержный генератор поведения реализации / неопределенности. Но это не считается «нормальным» преобразованием, поэтому не допускается (обычно) очень консервативным static_cast.
reinterpret_cast довольно часто встречается, когда вы работаете с системным программным обеспечением, таким как базы данных. В большинстве случаев вы пишете свой собственный менеджер страниц, который не знает, какой тип данных хранится на странице, и просто возвращает указатель void. На более высоких уровнях можно переосмыслить актерский состав и сделать вывод, как им заблагорассудится.
Первый пример опасен тем, что предполагает хорошее поведение со стороны вызывающего (всегда передавать указатель на реальный объект EventData и ничего больше). К сожалению, я не думаю, что есть какой-либо практический способ сколько-нибудь значимо проверить тип указателя на пустоту. В идеале аргумент должен быть строго типизированным. Просто некоторые наблюдения; не критика ответа.
@ BrianA.Henning Согласен. Я бы лично использовал reinterpret_cast<> в этой ситуации как «красный флаг», чтобы подчеркнуть ваше наблюдение, что вызывающая сторона несет большую ответственность за передачу «действительного» указателя.
@JohannGerell ваше утверждение неверно. Приведенный выше фрагмент на const_cast не имеет доказательств, вызывающих неопределенное поведение. Неопределенное поведение может быть вызвано наличием оператора, изменяющего значение. Использование const_cast для удаления constness допустимо, законно и нормально.
@truthadjustr, вы правы, как объясняется в разделе Ноты документа en.cppreference.com/w/cpp/language/const_cast. Я удалил свой комментарий.
В дополнение к другим ответам, приведенным на данный момент, вот неочевидный пример, когда static_cast недостаточно, поэтому необходим reinterpret_cast. Предположим, есть функция, которая в выходном параметре возвращает указатели на объекты разных классов (которые не имеют общего базового класса). Реальный пример такой функции - CoCreateInstance() (см. Последний параметр, которым на самом деле является void**). Предположим, вы запрашиваете у этой функции конкретный класс объекта, поэтому вы заранее знаете тип указателя (что вы часто делаете для COM-объектов). В этом случае вы не можете преобразовать указатель на ваш указатель в void** с помощью static_cast: вам нужен reinterpret_cast<void**>(&yourPointer).
В коде:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
Однако static_cast работает с простыми указателями (а не с указателями на указатели), поэтому приведенный выше код можно переписать, чтобы избежать использования reinterpret_cast (за счет дополнительной переменной), следующим образом:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
Разве не сработало бы что-то вроде &static_cast<void*>(pNetFwPolicy2) вместо static_cast<void**>(&pNetFwPolicy2)?
Это может помочь, если вы немного разбираетесь во внутреннем устройстве ...
static_cast
float, в int. Используйте для них static_cast.A в B, static_cast вызывает конструктор B, передавая A в качестве параметра. В качестве альтернативы, A может иметь оператор преобразования (например, A::operator B()). Если B не имеет такого конструктора или A не имеет оператора преобразования, вы получите ошибку времени компиляции.A* в B* всегда успешно, если A и B находятся в иерархии наследования (или недействительны), в противном случае вы получите ошибку компиляции.A& - B&.dynamic_cast
(Base*) к (Derived*) может завершиться ошибкой, если указатель на самом деле не является производным типом.A* в B*, если приведение недопустимо, то dynamic_cast вернет nullptr.A& в B&, если приведение недопустимо, то dynamic_cast вызовет исключение bad_cast.const_cast
set<T>, который возвращает только свои элементы как const, чтобы убедиться, что вы не изменили его ключ. Однако, если вы намереваетесь изменить неключевые элементы объекта, все должно быть в порядке. Вы можете использовать const_cast для удаления константности.T& SomeClass::foo(), а также const T& SomeClass::foo() const. Чтобы избежать дублирования кода, вы можете применить const_cast для возврата значения одной функции из другой.reinterpret_cast
float в 4 байта int, чтобы увидеть, как выглядят биты в float.Я добавил информацию об операторе преобразования, но есть еще несколько вещей, которые также следует исправить, и я не чувствую себя комфортно обновлять это слишком часто. Пункты следующие: 1. If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime. Вы получаете UB, что может привести к сбою во время выполнения, если вам повезет. 2. Динамические забросы также можно использовать при перекрестном забросе. 3. Константное приведение в некоторых случаях может привести к UB. Использование mutable может быть лучшим выбором для реализации логической константности.
@ Адриан, ты прав во всех отношениях. Ответ написан для людей более или менее начального уровня, и я не хотел перегружать их всеми другими сложностями, связанными с mutable, кросс-кастингом и т. д.
Хотя в других ответах хорошо описаны все различия между приведениями C++, я хотел бы добавить короткое замечание, почему вы не должны использовать приведения в стиле C (Type) var и Type(var).
Для новичков в C++ приведение типов в стиле C выглядит как надстройка над приведением типов в C++ (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()), и кто-то может предпочесть их приведению C++. . Фактически, приведение в стиле C - это надмножество, и писать его короче.
Основная проблема приведений в стиле C заключается в том, что они скрывают истинное намерение разработчика приведения типов. Приведения в стиле C могут выполнять практически все типы приведения, от обычно безопасных приведения, выполняемых static_cast <> () и dynamic_cast <> (), до потенциально опасных типов, таких как const_cast <> (), где модификатор const может быть удален, чтобы переменные const можно изменить и переинтерпретировать_каст <> (), который даже может переинтерпретировать целочисленные значения в указатели.
Вот образец.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
Основная причина, по которой в язык были добавлены преобразования C++, заключалась в том, чтобы позволить разработчику прояснить свои намерения - почему он собирается выполнять это преобразование. Используя приведение типов в стиле C, которые вполне допустимы в C++, вы делаете свой код менее читаемым и более подверженным ошибкам, особенно для других разработчиков, которые не создавали ваш код. Таким образом, чтобы сделать ваш код более читаемым и явным, вы всегда должны предпочесть приведение типов C++, а не приведение типов C.
Вот короткая цитата из книги Бьярна Страуструпа (автора C++) The C++ Programming Language 4th edition - page 302.
This C-style cast is far more dangerous than the named conversion operators because the notation is harder to spot in a large program and the kind of conversion intended by the programmer is not explicit.
Голосование за то, что ссылается на цитату Страуструпа. Трудно найти в наши дни, особенно то, что мы часто вместо этого слышали это от людей очень умно, а не от самого человека.
Чтобы понять это, давайте рассмотрим нижеприведенный фрагмент кода:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Только строка (4) компилируется без ошибок. Только reinterpret_cast можно использовать для преобразования указателя на объект в указатель на любой несвязанный тип объекта.
Следует отметить следующее: dynamic_cast не сработает во время выполнения, однако на большинстве компиляторов он также не сможет скомпилироваться, потому что в структуре приводимого указателя нет виртуальных функций, что означает, что dynamic_cast будет работать только с указателями полиморфных классов. .
Когда использовать приведение C++:
Предоставленный фрагмент - плохой пример. Хотя я согласен, что, действительно, компилируется. Список Когда является неопределенно правильным, но в основном наполнен мнениями, недостаточными для понимания требуемой детализации.
Может быть, здесь хорошая ссылка: Как вы объясните различия между static_cast, reinterpret_cast, const_cast и dynamic_cast новому программисту на C++?.