Поиск…


Вступление

Выражение может быть преобразовано в явном виде или преобразованный к типу T с помощью dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> или const_cast<T> , в зависимости от того, какого типа броска предназначен.

C ++ также поддерживает фрейм-нотацию в стиле функции, T(expr) и буквенную нотацию C-стиля, (T)expr .

Синтаксис

  • простой тип-спецификатор ( )
  • простой тип-спецификатор ( список выражений )
  • simple-type-specifier braced-init-list
  • typename-specifier ( )
  • typename-specifier ( список выражений )
  • typename-specifier braced-init-list
  • dynamic_cast < type-id > ( выражение )
  • static_cast < type-id > ( выражение )
  • reinterpret_cast < type-id > ( выражение )
  • const_cast < type-id > ( выражение )
  • ( тип-id ) литье-выражение

замечания

Все шесть нотных обозначений имеют одну общую черту:

  • Приведение к ссылочному типу lvalue, как и в dynamic_cast<Derived&>(base) , дает значение l. Поэтому, когда вы хотите что-то делать с одним и тем же объектом, но относитесь к нему как к другому типу, вы должны указывать тип ссылки lvalue.
  • Приведение к ссылочному типу rvalue, как и static_cast<string&&>(s) , дает значение r.
  • Приведение к не ссылочному типу, как в (int)x , дает значение prvalue, которое можно рассматривать как копию значения, которое выполняется, но с другим типом от оригинала.

Ключевое слово reinterpret_cast отвечает за выполнение двух разных «небезопасных» преобразований:

Ключевое слово static_cast может выполнять множество различных преобразований:

  • Основание для производных преобразований

  • Любое преобразование, которое может быть выполнено путем прямой инициализации, включая как неявные преобразования, так и преобразования, которые вызывают явный конструктор или функцию преобразования. См. Здесь и здесь для более подробной информации.

  • void , которое отбрасывает значение выражения.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Между арифметическими и перечисляемыми типами и между различными типами перечислений. См. Переходы перечисления

  • От указателя к члену производного класса, чтобы указатель на член базового класса. Указанные типы должны совпадать. См. Производное преобразование базы для указателей на участников

  • void* to T* .

C ++ 11

Основание для производной конверсии

Указатель на базовый класс может быть преобразован в указатель на производный класс с использованием static_cast . static_cast не выполняет проверку во время выполнения и может привести к неопределенному поведению, когда указатель фактически не указывает на нужный тип.

struct Base {};
struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1;                        // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
                                         // point to a Derived object

Аналогично, ссылка на базовый класс может быть преобразована в ссылку на производный класс с использованием static_cast .

struct Base {};
struct Derived : Base {};
Derived d;
Base& r1 = d;
Derived& r2 = r1;                        // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object

Если тип источника является полиморфным, dynamic_cast может использоваться для преобразования базы в производную. Он выполняет проверку времени выполнения, а отказ восстанавливается, а не создает неопределенное поведение. В случае с указателем при ошибке возвращается нулевой указатель. В ссылочном случае исключение вызывается при ошибке типа std::bad_cast (или класса, полученного из std::bad_cast ).

struct Base { virtual ~Base(); }; // Base is polymorphic
struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Изгнание

Указатель на объект const может быть преобразован в указатель на объект, не const_cast с использованием const_cast слова const_cast . Здесь мы используем const_cast для вызова функции, которая не является const_cast . Он принимает только аргумент non-const char* хотя он никогда не пишет через указатель:

void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s);                    // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*

const_cast для ссылочного типа может быть использован для преобразования значения const_cast с const_cast в значение, не соответствующее const_cast .

const_cast опасен, поскольку он не позволяет системе типа C ++ не пытаться изменить объект const. Это приводит к неопределенному поведению.

const int x = 123;
int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Преобразование типа punning

Указатель (соответственно ссылка) на тип объекта может быть преобразован в указатель (соотв. Ссылку) на любой другой тип объекта, используя reinterpret_cast . Это не вызывает никаких конструкторов или функций преобразования.

int x = 42;
char* p = static_cast<char*>(&x);      // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z';                              // maybe this modifies x (see below)
C ++ 11

Результат reinterpret_cast представляет тот же адрес, что и операнд, при условии, что адрес соответствующим образом выровнен для целевого типа. В противном случае результат не указан.

int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11

Результат reinterpret_cast не указан, за исключением того, что указатель (соответствующая ссылка) выдержит круговую поездку от типа источника до типа назначения и обратно, если требование выравнивания типа назначения не является более строгим, чем требование типа источника.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

В большинстве реализаций reinterpret_cast не изменяет адрес, но это требование не было стандартизировано до C ++ 11.

reinterpret_cast также может использоваться для преобразования из одного типа-указателя-данных в другой или одного типа-указателя-члена в другой.

Использование reinterpret_cast считается опасным, поскольку чтение или запись через указатель или ссылку, полученные с использованием reinterpret_cast могут вызывать неопределенное поведение, когда исходный и целевой типы не связаны.

Преобразование между указателем и целым числом

Указатель объекта (включая void* ) или указатель функции может быть преобразован в целочисленный тип с использованием reinterpret_cast . Это будет скомпилировано только в том случае, если тип назначения достаточно длинный. Результат определяется реализацией и обычно дает числовой адрес байта в памяти, на который указывает указатель.

Как правило, long или unsigned long достаточно длинный, чтобы удерживать любое значение указателя, но это не гарантируется стандартом.

C ++ 11

Если существуют типы std::intptr_t и std::uintptr_t , они гарантированно будут достаточно длинными, чтобы удерживать void* (и, следовательно, любой указатель на тип объекта). Однако они не гарантируют, что они будут достаточно длинными, чтобы удерживать указатель на функцию.

Аналогично, reinterpret_cast может использоваться для преобразования целочисленного типа в тип указателя. Опять же результат определяется реализацией, но значение указателя гарантируется неизменным путем кругового перехода через целочисленный тип. Стандарт не гарантирует, что значение нуля преобразуется в нулевой указатель.

void register_callback(void (*fp)(void*), void* arg); // probably a C API
void my_callback(void* x) {
    std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
                  reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Преобразование с помощью явного конструктора или явной функции преобразования

Преобразование, которое включает вызов явного конструктора или функции преобразования, не может выполняться неявно. Мы можем запросить, чтобы преобразование выполнялось явно с использованием static_cast . Значение такое же, как и для прямой инициализации, за исключением того, что результат является временным.

class C {
    std::unique_ptr<int> p;
  public:
    explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
    f(p);                 // error: C::C(int*) is explicit
    f(static_cast<C>(p)); // ok
    f(C(p));              // equivalent to previous line
    C c(p); f(c);         // error: C is not copyable
}

Неявное преобразование

static_cast может выполнять любое неявное преобразование. Иногда использование static_cast может быть полезным, например, в следующих примерах:

  • При передаче аргументов в эллипсис «ожидаемый» тип аргумента не статически известен, поэтому не происходит никакого неявного преобразования.

    const double x = 3.14;
    printf("%d\n", static_cast<int>(x)); // prints 3
    // printf("%d\n", x); // undefined behaviour; printf is expecting an int here
    // alternative:
    // const int y = x; printf("%d\n", y);
    

    Без явного преобразования типов double объект будет передан эллипсису, и произойдет неопределенное поведение.

  • Оператор присваивания производного класса может вызвать оператор присваивания базового класса следующим образом:

    struct Base { /* ... */ };
    struct Derived : Base {
        Derived& operator=(const Derived& other) {
            static_cast<Base&>(*this) = other;
            // alternative:
            // Base& this_base_ref = *this; this_base_ref = other;
        }
    };
    

Конверсии Enum

static_cast может преобразовывать из целого числа или типа с плавающей точкой в ​​тип перечисления (независимо от того, областны ли они или не определены) и наоборот. Он также может преобразовывать типы перечислений.

  • Преобразование из неперечисленного типа перечисления в арифметический тип является неявным преобразованием; можно, но не обязательно, использовать static_cast .
C ++ 11
  • Когда тип перечислимого типа конвертируется в арифметический тип:

    • Если значение перечисления может быть представлено точно в целевом типе, результатом будет это значение.
    • В противном случае, если тип назначения является целым типом, результат не указан.
    • В противном случае, если тип назначения является типом с плавающей точкой, результат будет таким же, как и для преобразования в базовый тип, а затем в тип с плавающей точкой.

    Пример:

    enum class Format {
        TEXT = 0,
        PDF = 1000,
        OTHER = 2000,
    };
    Format f = Format::PDF;
    int a = f;                         // error
    int b = static_cast<int>(f);       // ok; b is 1000
    char c = static_cast<char>(f);     // unspecified, if 1000 doesn't fit into char
    double d = static_cast<double>(f); // d is 1000.0... probably
    
  • Когда целочисленный или перечисляемый тип преобразуется в тип перечисления:

    • Если исходное значение находится в пределах диапазона адресата, результатом является это значение. Обратите внимание, что это значение может быть неравным для всех счетчиков.
    • В противном случае результат не указан (<= C ++ 14) или undefined (> = C ++ 17).

    Пример:

    enum Scale {
        SINGLE = 1,
        DOUBLE = 2,
        QUAD = 4
    };
    Scale s1 = 1;                     // error
    Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
    Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
    Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
    
C ++ 11
  • Когда тип с плавающей точкой преобразуется в тип перечисления, результат будет таким же, как преобразование в базовый тип перечисления, а затем в тип перечисления.

    enum Direction {
        UP = 0,
        LEFT = 1,
        DOWN = 2,
        RIGHT = 3,
    };
    Direction d = static_cast<Direction>(3.14); // d is RIGHT
    

Производится преобразование базы для указателей на членов

Указатель на член производного класса может быть преобразован в указатель на член базового класса с использованием static_cast . Указанные типы должны совпадать.

Если операнд является нулевым указателем на значение члена, результат также является нулевым указателем на значение члена.

В противном случае преобразование будет действительным только в том случае, если элемент, на который указывает операнд, фактически существует в целевом классе, или если целевой класс является базовым или производным классом класса, содержащего член, на который указывает операнд. static_cast не проверяет достоверность. Если преобразование недействительно, поведение не определено.

struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1;                              // ok; implicit conversion
int B::*p3 = p2;                              // error
int B::*p4 = static_cast<int B::*>(p2);       // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2);       // undefined; p2 points to x, which is a member
                                              // of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6);       // error: types don't match

void * to T *

В C ++ void* не может быть неявно преобразован в T* где T - тип объекта. Вместо этого static_cast следует использовать для явного преобразования. Если операнд фактически указывает на объект T , результат указывает на этот объект. В противном случае результат не указан.

C ++ 11

Даже если операнд не указывает на объект T , если операнд указывает на байт, адрес которого правильно выровнен для типа T , результат преобразования указывает на тот же байт.

// allocating an array of 100 ints, the hard way
int* a = malloc(100*sizeof(*a));                    // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100];                           // no cast needed
// std::vector<int> a(100);                         // better

const char c = '!';
const void* p1 = &c;
const char* p2 = p1;                           // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1);   // unspecified in C++03;
                                               // possibly unspecified in C++11 if
                                               // alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1);             // error: casting away constness

Литье в стиле C

Кастинг C-Style можно рассматривать как «Лучшее усилие» и назван так, что он является единственным литым, который может быть использован в C. Синтаксис этого (NewType)variable - (NewType)variable .

Всякий раз, когда этот приведение используется, он использует одну из следующих c ++-трансляций (по порядку):

  • const_cast<NewType>(variable)
  • static_cast<NewType>(variable)
  • const_cast<NewType>(static_cast<const NewType>(variable))
  • reinterpret_cast<const NewType>(variable)
  • const_cast<NewType>(reinterpret_cast<const NewType>(variable))

Функциональное литье очень похоже, но в качестве нескольких ограничений в результате его синтаксиса: NewType(expression) . В результате могут быть применены только типы без пробелов.

Лучше использовать новую c ++-трансляцию, потому что она более читаема и может быть легко обнаружена в любом месте исходного кода на C ++, и ошибки будут обнаружены во время компиляции, а не во время выполнения.

Поскольку этот приведение может привести к непреднамеренному reinterpret_cast , его часто считают опасным.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow