Spec-Zone.ru › C++

Явное преобразование типов

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

Синтаксис

( целевой тип ) выражение (1)
целевой тип ( список выражений (необязательно) ) (2)
целевой тип { список выражений (необязательно) } (3) (с C++11)
имя шаблона ( список выражений (необязательно) ) (4) (с C++17)
имя шаблона { список выражений (необязательно) } (5) (с C++17)
auto ( выражение ) (6) (с C++23)
auto { выражение } (7) (с C++23)

Возвращает значение типа целевой тип.

Объяснение

1) При встрече выражения приведения типов в стиле C, компилятор пытается интерпретировать его как следующие выражения приведения в данном порядке:
a) const_cast<target-type>(expression);
b) static_cast<target-type>(expression), с расширениями: указатель или ссылка на производный класс дополнительно разрешено привести к указателю или ссылке на недвусмысленный базовый класс (и наоборот), даже если базовый класс недоступен (то есть, это приведение игнорирует спецификатор закрытого наследования). То же самое относится к приведению указателя на член к указателю на член недвусмысленного невиртуального базового класса;
c) static_cast (с расширениями) за которым следует const_cast;
d) reinterpret_cast<target-type>(expression);
e) reinterpret_cast за которым следует const_cast.
Первый вариант, удовлетворяющий требованиям соответствующего оператора приведения, выбирается, даже если он не может быть скомпилирован (см. пример). Если приведение может быть интерпретировано более чем одним способом как static_cast за которым следует const_cast, оно не может быть скомпилировано.
Кроме того, обозначение приведения в стиле C допускает приведение от, к и между указателями на неполный тип класса. Если и выражение и целевой тип являются указателями на неполные типы класса, не определено, static_cast или reinterpret_cast будет выбрано.
2) Выражение приведения в функциональном стиле состоит из простого спецификатора типа или спецификатора typedef (иными словами, имени типа из одного слова, то есть такие случаи, как unsigned int(expression) и int*(expression) не являются допустимыми), за которым следует список выражений, разделенных запятыми, в скобках.
  • Если в скобках находится ровно одно выражение, это выражение приведения точно эквивалентно соответствующему выражению приведения в стиле C.
  • Если в скобках находится более одного выражения или список инициализации(с C++11) , целевой тип должен быть классом с соответствующим объявленным конструктором. Это выражение представляет собой prvalue типа целевой тип обозначающее временное значение(до C++17)объект результата которого (с C++17) прямо инициализируется списком список выражений.
  • Если в скобках нет выражения: если целевой тип называет не массивный полный тип объекта, это выражение представляет собой prvalue типа целевой тип, обозначающее временное значение(до C++17)объект результата которого (возможно, с добавленными cv-квалификаторами)(с C++17) этого типа. Если целевой тип является типом объекта, объект инициализируется по умолчанию. Если целевой тип (возможно cv-квалифицированный) void, выражение представляет собой prvalue типа void без объекта результата(с C++17).
3) Имя типа из одного слова, за которым следует список инициализации, представляет собой prvalue указанного типа обозначающее временное значение(до C++17)объект результата которого (с C++17) прямо инициализируется заданным списком инициализации. Если целевой тип является (возможно cv-квалифицированным) void, выражение представляет собой void prvalue без объекта результата(с C++17). Это единственное выражение приведения, которое может создать массив prvalue.(до C++20)
4,5) Аналогично (2,3), за исключением того, что сначала выполняется вывод аргументов шаблона класса.
6,7) Спецификатор auto заменяется на выведенный тип вымышленной переменной x объявленной с auto x(expression); (которая никогда не интерпретируется как объявление функции) или auto x{expression};, соответственно. Результат всегда представляет собой prvalue объекта типа.

Как и во всех выражениях приведения, результат:

  • является lvalue, если целевой тип является типом lvalue ссылки или rvalue ссылкой на тип функции(с C++11);
  • является xvalue, если целевой тип является rvalue ссылкой на тип объекта;
(с C++11)
  • является prvalue в противном случае.

Разрешение неоднозначностей

Неоднозначное объявление оператора

В случае неоднозначности между выражением оператора с функциональным приведением как его левым подвыражением и оператором объявления, неоднозначность разрешается, рассматривая его как оператор объявления. Это разрешение неоднозначности чисто синтаксическое: оно не учитывает значение имен, встречающихся в операторе, кроме того, являются ли они именами типов:

struct M {};
struct L { L(M&); };
 
M n;
void f()
{
    M(m);    // declaration, equivalent to M m;
    L(n);    // ill-formed declaration, equivalent to L n;
    L(l)(m); // still a declaration, equivalent to L l((m));
}

Однако, если внешний декларатор в неоднозначном операторе объявления имеет тип возврата с хвостом, оператор будет рассматриваться только как оператор объявления, если тип возврата с хвостом начинается с auto:

struct M;
 
struct S
{
    S* operator()();
    int N;
    int M;
 
    void mem(S s)
    {
        auto(s)()->M; // expression (S::M hides ::M), invalid before C++23
    }
};
 
void f(S s)
{
    {
        auto(s)()->N; // expression, invalid before C++23
        auto(s)()->M; // function declaration, equivalent to M s();
    }
    {
        S(s)()->N;    // expression
        S(s)()->M;    // expression
    }
}
(с C++11)

Неоднозначный параметр функции

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

struct S
{
    S(int);
};
 
void foo(double a)
{
    S w(int(a)); // function declaration: has a parameter `a` of type int
    S x(int());  // function declaration: has an unnamed parameter of type int(*)() 
                 // that is adjusted from int()
 
    // Ways to avoid ambiguity:
    S y((int(a))); // object declaration: extra pair of parentheses
    S y((int)a);   // object declaration: C-style cast
    S z = int(a);  // object declaration: no ambiguity for this syntax
}

Однако, если внешний декларатор в неоднозначном параметре объявления имеет тип возврата с хвостом, неоднозначность будет разрешена только как объявление, если она начинается с auto:

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    S a(B()->C);    // object declaration: B()->C cannot declare a parameter
    S b(auto()->C); // function declaration: has an unnamed parameter of type C(*)()
                    // that us adjusted from C()
}
(с C++11)

Неоднозначный type-id

Неоднозначность может возникнуть из-за сходства между функциональным приведением и type-id. Решение состоит в том, что любая конструкция, которая потенциально может быть type-id в её синтаксическом контексте, должна рассматриваться как type-id:

// `int()` and `int(unsigned(a))` can both be parsed as type-id:
// `int()`            represents a function returning int
//                    and taking no argument
// `int(unsigned(a))` represents a function returning int
//                    and taking an argument of type unsigned
void foo(signed char a)
{
    sizeof(int());            // type-id (ill-formed)
    sizeof(int(a));           // expression
    sizeof(int(unsigned(a))); // type-id (ill-formed)
 
    (int()) + 1;            // type-id (ill-formed)
    (int(a)) + 1;           // expression
    (int(unsigned(a))) + 1; // type-id (ill-formed)
}

Однако, если внешний абстрактный дескриптор в неоднозначном идентификаторе типа имеет возвращаемый тип в конце, неоднозначность будет разрешена только путём его обработки как идентификатор типа, если он начинается с auto:

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(expression)
    sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}
(с C++11)

Примечания

Макрос проверки возможностей Значение Стандарт Функция
__cpp_auto_cast 202110L (C++23) auto(x) и auto{x}

Пример

#include <cassert>
#include <iostream>
 
double f = 3.14;
unsigned int n1 = (unsigned int)f; // C-style cast
unsigned int n2 = unsigned(f);     // function-style cast
 
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // casts incomplete type to incomplete type
}
 
void cpp23_decay_copy_demo()
{
    auto inc_print = [](int& x, const int& y)
    {
        ++x;
        std::cout << "x:" << x << ", y:" << y << '\n';
    };
 
    int p{1};
    inc_print(p, p); // prints x:2 y:2, because param y here is an alias of p
    int q{1};
    inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue,
                           // so the param y is a copy of q (not an alias of q)
}
 
// In this example, C-style cast is interpreted as static_cast
// even though it would work as reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
 
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // compile-time error
    A* a = reinterpret_cast<A*>(d); // this compiles
    assert(a == nullptr);
 
    cpp23_decay_copy_demo();
}

Вывод:

x:2 y:2
x:2 y:1

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющих поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

Отчёт об ошибке Применено к Поведение, как опубликовано Правильное поведение
CWG 1223
(P2915R0)
C++11 добавление возвращаемого типа в конце привело к новым неоднозначностям разрешает их
CWG 2620 C++98 разрешение неоднозначных параметров функции может быть неправильно истолковано улучшена формулировка

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2023):
    • 7.6.1.4 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 7.6.3 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++20 (ISO/IEC 14882:2020):
    • 7.6.1.4 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 7.6.3 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++17 (ISO/IEC 14882:2017):
    • 8.2.3 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 8.4 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++14 (ISO/IEC 14882:2014):
    • 5.2.3 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 5.4 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++11 (ISO/IEC 14882:2011):
    • 5.2.3 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 5.4 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++03 (ISO/IEC 14882:2003):
    • 5.2.3 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 5.4 Явное преобразование типа (запись в виде каста) [expr.cast]
  • Стандарт C++98 (ISO/IEC 14882:1998):
    • 5.2.3 Явное преобразование типа (функциональная запись) [expr.type.conv]
    • 5.4 Явное преобразование типа (запись в виде каста) [expr.cast]

См. также

Преобразование const_cast добавляет или удаляет const
Преобразование static_cast выполняет основные преобразования
Преобразование dynamic_cast выполняет проверенные полиморфные преобразования
Преобразование reinterpret_cast выполняет общие преобразования низкого уровня
Стандартные преобразования неявные преобразования из одного типа в другой
Документация C для оператора каста

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/explicit_cast

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API