Можно ли преобразовать nullptr в uintptr_t? Разные компиляторы не согласны - PullRequest
10 голосов
/ 03 марта 2020

Рассмотрим эту программу:

#include <cstdint>
using my_time_t = uintptr_t;

int main() {
    const my_time_t t = my_time_t(nullptr);
}

Не удалось скомпилировать с msv c v19.24:

<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'

Compiler returned: 2

, но clang (9.0.1) и g cc (9.2.1) «съесть» этот код без ошибок.

Мне нравится поведение MSV C, но подтверждается ли оно стандартом? Другими словами, это ошибка в clang / g cc или возможно интерпретировать стандарт, что это правильное поведение от gcc / clang?

Ответы [ 3 ]

5 голосов
/ 03 марта 2020

По моему мнению, MSV C не соответствует стандартам.

Я основываю этот ответ на C ++ 17 (черновик N4659), но C ++ 14 и C ++ 11 имеют эквивалент

my_time_t(nullptr) - это постфиксное выражение , а поскольку my_time_t - это тип, а (nullptr) - это отдельное выражение в списке инициализаторов в скобках, оно в точности эквивалентно явное приведение выражения. ( [expr.type.conv] / 2 )

При явном приведении выполняется несколько различных спецификаций c C ++ приведений (с расширениями), в частности также reinterpret_cast. ( [expr. целочисленный тип.

Но reinterpret_cast<my_time_t>(nullptr) должен быть успешным, потому что [expr.reinterpret.cast] / 4 говорит, что значение типа std::nullptr_t может быть преобразовано в целочисленный тип, как если бы reinterpret_cast<my_time_t>((void*)0), что возможно, потому что my_time_t = std::uintptr_t должен быть достаточно большим типом, чтобы представлять все значения указателя, и при этом условии тот же стандартный абзац позволяет преобразовать void* в целочисленный тип.

особенно странно, что MSV C разрешает преобразование, если используется приведенная, а не функциональная запись:

const my_time_t t = (my_time_t)nullptr;
2 голосов
/ 03 марта 2020

Хотя я не могу найти явного упоминания в этом рабочем проекте стандарта C ++ (с 2014 года) о том, что преобразование из std::nullptr_t в целочисленный тип запрещено, также нет упоминания что такое преобразование разрешено!

Тем не менее, случай преобразования из std::nullptr_t в bool явно указан :

4.12 Булевы преобразования Значение арифметического c, перечисление с незаданной областью, указатель или указатель на тип элемента может быть преобразовано в значение типа bool. Нулевое значение, нулевое значение указателя или нулевое значение указателя члена преобразуется в ложь; любое другое значение преобразуется в true. Для прямой инициализации (8.5) значение типа std :: nullptr_t может быть преобразовано в значение типа bool; полученное значение равно false.

Кроме того, место only в этом черновом документе, где упоминается преобразование из std::nullptr_t в целочисленный тип, находится в разделе «reinterpret_cast». :

5.2.10 Повторная интерпретация приведения ... (4) Указатель может быть явно преобразован в любой целочисленный тип, достаточно большой для его хранения. Функция отображения определяется реализацией. [Примечание: он предназначен для тех, кто знает структуру адресации базовой машины. - примечание конца] Значение типа std :: nullptr_t может быть преобразовано в целочисленный тип; преобразование имеет то же значение и действительность, что и преобразование (void *) 0 в целочисленный тип. [Примечание: reinterpret_cast нельзя использовать для преобразования значения любого типа в тип std :: nullptr_t. - примечание к концу]

Итак, из этих двух наблюдений можно (ИМХО) разумно предположить, что компилятор MSVC верен.

РЕДАКТИРОВАТЬ : Тем не менее, использование вами «функциональной нотации» может фактически свидетельствовать об обратном! У компилятора MSVC нет проблем с использованием приведения типа C, например:

uintptr_t answer = (uintptr_t)(nullptr);

, но (как в вашем коде) он жалуется на это:

uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'

Тем не менее, из того же проекта стандарта:

5.2.3 Явное преобразование типов (функциональная запись) (1) Спецификатор простого типа (7.1.6.2) или спецификатор типа (14.6), за которым следует список выражений в скобках, создает значение указанного типа по заданному списку выражений. Если список выражений является одним выражением, выражение преобразования типа эквивалентно (в определенности и если определено в значении) соответствующему приведенному выражению (5.4). ...

«Соответствующее выражение приведения (5.4)» может относиться к приведению в стиле C.

0 голосов
/ 03 марта 2020

Все стандартно соответствуют (см. Черновик n4659 для C ++).

nullptr определяется в [lex.nullptr] как:

Литерал указателя является ключевым словом nullptr. Это значение типа std :: nullptr_t. [Примечание: ... значение этого типа является константой нулевого указателя и может быть преобразовано в значение нулевого указателя или значение указателя нулевого элемента.]

Даже если примечания не являются нормативными, это ясно, что для стандарта nullptr ожидается преобразование в нулевое указатель значение.

Мы позже найдем в [conv.ptr]:

Константа нулевого указателя является целочисленным литералом со значением ноль или значением типа std :: nullptr_t. Константа нулевого указателя может быть преобразована в тип указателя; .... Константа нулевого указателя целочисленного типа может быть преобразована в значение типа std :: nullptr_t.

И снова стандартом является то, что 0 можно преобразовать в std::nullptr_t и что nullptr может быть преобразовано в любой тип указателя.

Мое чтение состоит в том, что стандарт не требует, чтобы nullptr можно было напрямую преобразовать в целое типа или нет. С этого момента:

  • MSV C имеет строгое чтение и запрещает преобразование
  • Clang и g cc ведет себя так, как если бы происходило промежуточное преобразование void *.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...