0 vs NULL vs nullptr
От: RiNSpy  
Дата: 01.12.16 11:19
Оценка:
Читаю Мейерса, возник базовый вопрос, который наверное и задать стыдно. Но тем не менее. Не совсем понимаю, что за типы у 0 и NULL. Почему в коде ниже не компилируются закомментированные строки?

http://cpp.sh/5nl7o

#include <iostream>
#include <string>

int f1(int* p)
{
    if (p == nullptr) return -1;
    else return *p;
}

template<typename funcPtr, typename paramPtr>
decltype(auto) runFunc(funcPtr f, paramPtr p)
{
    return (*f)(p);
}

int main()
{
  std::cout<<"Template:"<<std::endl;
  //std::cout<<runFunc(&f1, 0)<<std::endl;    // Doesn't compile
  //std::cout<<runFunc(&f1, NULL)<<std::endl; // Doesn't compile
  std::cout<<runFunc(&f1, nullptr)<<std::endl;
  std::cout<<"No template:"<<std::endl;
  std::cout<<f1(0)<<std::endl;
  std::cout<<f1((int)0)<<std::endl;
  std::cout<<f1(NULL)<<std::endl;
  std::cout<<f1((int)NULL)<<std::endl;
  std::cout<<f1(nullptr)<<std::endl;
}



Выходит, просто 0 или NULL компилятор может сконверитировать в указатель; (int)0 и (int)NULL тоже может. Но если 0 или NULL передаются как параметр функции типа int, то компилятор уже не может превратить их в указатель? В чём разница?
Re: 0 vs NULL vs nullptr
От: uzhas Ниоткуда  
Дата: 01.12.16 11:41
Оценка: 4 (1) +1
Здравствуйте, RiNSpy, Вы писали:

RNS>Выходит, просто 0 или NULL компилятор может сконверитировать в указатель; (int)0 и (int)NULL тоже может.

эти два выражения не должны конвертироваться в указатель. у них тип int

RNS>Но если 0 или NULL передаются как параметр функции типа int, то компилятор уже не может превратить их в указатель? В чём разница?


литеральный 0 может преобразовываться в указатель. за это отвечает не система типов, а компилятор. то есть он видит литеральный ноль + видит передачу указателя -> он передает нулевой указатель
в других ситуациях 0 является константой с типом int
варианты из вашего примера, которые также не должны компилироваться:
std::cout<<f1((int)0)<<std::endl;
std::cout<<f1((int)NULL)<<std::endl;


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

NULL — это некоторая константа, которая умеет преобразовываться к нулевому указателю. ее тип — implementation defined. бывает int (литеральный 0), а бывает void*

собственно, чтобы немного уменьшить кол-во зоопарка и ввели nullptr, у которого есть даже свой тип — nullptr_t

в этих двух примерах
RNS>  //std::cout<<runFunc(&f1, 0)<<std::endl;    // Doesn't compile
RNS>  //std::cout<<runFunc(&f1, NULL)<<std::endl; // Doesn't compile

выведенный тип для 0 и NULL не совпадает с int*, поэтому код не компилируется
Отредактировано 01.12.2016 11:51 uzhas . Предыдущая версия . Еще …
Отредактировано 01.12.2016 11:43 uzhas . Предыдущая версия .
Re[2]: 0 vs NULL vs nullptr
От: RiNSpy  
Дата: 01.12.16 12:15
Оценка:
Здравствуйте, uzhas, Вы писали:

U>литеральный 0 может преобразовываться в указатель. за это отвечает не система типов, а компилятор. то есть он видит литеральный ноль + видит передачу указателя -> он передает нулевой указатель

U>в других ситуациях 0 является константой с типом int

Т.е. это специальный хак такой, именно для литерального 0?
Re[2]: 0 vs NULL vs nullptr
От: N. I.  
Дата: 01.12.16 12:33
Оценка: 2 (1) +1
uzhas:

U>нюанс с литеральным нулем очень важен. в компиляторах есть баги, когда они даже constexpr выражение 1-1 способны преобразовывать к нулевому указателю, однако не должны


Это не баг, а вполне себе правильное для C++98 — C++11 поведение. Разработчики просто не успели или не захотели реализовать breaking change из C++14 (которое потенциально может привести к появлению реальных багов в старых программах).

U>NULL — это некоторая константа, которая умеет преобразовываться к нулевому указателю. ее тип — implementation defined. бывает int (литеральный 0), а бывает void*


void* может быть в C, но не в C++.
Re[3]: 0 vs NULL vs nullptr
От: uzhas Ниоткуда  
Дата: 01.12.16 13:08
Оценка: 9 (2)
Здравствуйте, N. I., Вы писали:

NI>Это не баг, а вполне себе правильное для C++98 — C++11 поведение.


для С++14 это вроде баг
решил на уровне стандарта найти разницу, вроде нашел:

С++11

4.10 Pointer conversions [conv.ptr]
1 A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to
zero
or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the
result is the null pointer value of that type and is distinguishable from every other value of pointer to object
or pointer to function type. Two null pointer values of the same type shall compare equal. The conversion
of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a
pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can
be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value.
—end note ]


С++14

4.10 Pointer conversions [conv.ptr]
1 A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.
A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type
and is distinguishable from every other value of object pointer or function pointer type. Such a conversion
is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The
conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the
sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of
integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a
null pointer value. —end note ]


NI>void* может быть в C, но не в C++.

хотел подчеркнуть, что стандартом не регламентируется какого типа NULL. вроде зафиксировано, что это макрос, но в доках не нашел =\
Отредактировано 01.12.2016 13:12 uzhas . Предыдущая версия . Еще …
Отредактировано 01.12.2016 13:11 uzhas . Предыдущая версия .
Re[4]: 0 vs NULL vs nullptr
От: N. I.  
Дата: 01.12.16 14:05
Оценка:
uzhas:

NI>>Это не баг, а вполне себе правильное для C++98 — C++11 поведение.


U>для С++14 это вроде баг


Багами в программе (в данном случае — компиляторе) обычно называют ошибки, из-за которых она работает не так, как было задумано разработчиком. Если багом называть любое nonconformance с неким новым стандартом, то любой старый компилятор, который разрабатывался для поддержки C++03 или C++11, внезапно станет ну просто-таки суперзабагованным.

NI>>void* может быть в C, но не в C++.

U>хотел подчеркнуть, что стандартом не регламентируется какого типа NULL. вроде зафиксировано, что это макрос, но в доках не нашел =\

C++98/C++03 пункт 18.1.
C++11/C++14 пункт 18.2.
Re[4]: 0 vs NULL vs nullptr
От: Sheridan Россия  
Дата: 12.12.16 07:28
Оценка:
Здравствуйте, uzhas, Вы писали:

U>хотел подчеркнуть, что стандартом не регламентируется какого типа NULL. вроде зафиксировано, что это макрос, но в доках не нашел =\


Можно и не в доках...
GCC
stddef.h
/* A null pointer constant.  */

#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL             /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */
#endif  /* G++ */
#endif    /* NULL not defined and <stddef.h> or need NULL.  */
#undef    __need_NULL


libio.h
#ifndef NULL
# if defined __GNUG__ && \
    (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define NULL (__null)
# else
#  if !defined(__cplusplus)
#   define NULL ((void*)0)
#  else
#   define NULL (0)
#  endif
# endif
#endif
Matrix has you...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.