Estou curioso para saber como nullptr
funciona. Os padrões N4659 e N4849 dizem:
- tem que ter tipo
std::nullptr_t
; - você não pode pegar o endereço dele;
- pode ser convertido diretamente em um ponteiro e ponteiro em membro;
sizeof(std::nullptr_t) == sizeof(void*)
;- sua conversão para
bool
éfalse
; - seu valor pode ser convertido para o tipo integral de forma idêntica
(void*)0
, mas não para trás;
Portanto, é basicamente uma constante com o mesmo significado que (void*)0
, mas tem um tipo diferente. Eu encontrei a implementação std::nullptr_t
no meu dispositivo e é a seguinte.
#ifdef _LIBCPP_HAS_NO_NULLPTR
_LIBCPP_BEGIN_NAMESPACE_STD
struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
void* __lx;
struct __nat {int __for_bool_;};
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}
template <class _Tp>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
operator _Tp* () const {return 0;}
template <class _Tp, class _Up>
_LIBCPP_INLINE_VISIBILITY
operator _Tp _Up::* () const {return 0;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}
#define nullptr _VSTD::__get_nullptr_t()
_LIBCPP_END_NAMESPACE_STD
#else // _LIBCPP_HAS_NO_NULLPTR
namespace std
{
typedef decltype(nullptr) nullptr_t;
}
#endif // _LIBCPP_HAS_NO_NULLPTR
Estou mais interessado na primeira parte. Parece satisfazer os pontos 1 a 5, mas não tenho idéia do porquê de ter uma subclasse __nat e tudo relacionado a ela. Eu também gostaria de saber por que ele falha em conversões integrais.
struct nullptr_t2{
void* __lx;
struct __nat {int __for_bool_;};
constexpr nullptr_t2() : __lx(0) {}
constexpr nullptr_t2(int __nat::*) : __lx(0) {}
constexpr operator int __nat::*() const {return 0;}
template <class _Tp>
constexpr
operator _Tp* () const {return 0;}
template <class _Tp, class _Up>
operator _Tp _Up::* () const {return 0;}
friend constexpr bool operator==(nullptr_t2, nullptr_t2) {return true;}
friend constexpr bool operator!=(nullptr_t2, nullptr_t2) {return false;}
};
inline constexpr nullptr_t2 __get_nullptr_t2() {return nullptr_t2(0);}
#define nullptr2 __get_nullptr_t2()
int main(){
long l = reinterpret_cast<long>(nullptr);
long l2 = reinterpret_cast<long>(nullptr2); // error: invalid type conversion
bool b = nullptr; // warning: implicit conversion
// edditor error: a value of type "std::nullptr_t" cannot be used to initialize an entity of type "bool"
bool b2 = nullptr2;
if (nullptr){}; // warning: implicit conversion
if (nullptr2){};
};
c++
c++17
nullptr
null-pointer
Fullfungo
fonte
fonte
nullptr_t
é um tipo fundamental. Como éint
implementado?#ifdef _LIBCPP_HAS_NO_NULLPTR
. Isso parece ser uma solução alternativa de melhor esforço para quando o compilador não fornecenullptr
.nullptr_t
é um tipo fundamental. Implementá-lo como um tipo de classe não faz uma implementação em conformidade. Veja o comentário de chris.is_class
eis_null_pointer
ambos não podem ser verdadeiros para o mesmo tipo. Somente uma das funções da categoria de tipo principal pode retornar true para um tipo específico.Respostas:
Funciona da maneira mais simples possível: por decreto . Funciona porque o padrão C ++ diz que funciona e funciona da mesma maneira porque o padrão C ++ diz que as implementações devem fazê-lo funcionar dessa maneira.
É importante reconhecer que é impossível implementar
std::nullptr_t
usando as regras da linguagem C ++. A conversão de uma constante de ponteiro nulo do tipostd::nullptr_t
para um ponteiro não é uma conversão definida pelo usuário. Isso significa que você pode passar de uma constante de ponteiro nula para um ponteiro e, em seguida, através de uma conversão definida pelo usuário para outro tipo, tudo em uma única sequência de conversão implícita.Isso não é possível se você implementar
nullptr_t
como uma classe. Os operadores de conversão representam conversões definidas pelo usuário, e as regras implícitas da sequência de conversão do C ++ não permitem mais de uma conversão definida pelo usuário nessa sequência.Portanto, o código que você postou é uma boa aproximação
std::nullptr_t
, mas não é nada além disso. Não é uma implementação legítima do tipo. Provavelmente, era de uma versão mais antiga do compilador (deixada por motivos de compatibilidade com versões anteriores) antes de o compilador fornecer suporte adequadostd::nullptr_t
. Você pode ver isso pelo fato de que ele#define
énullptr
, ao mesmo tempo C ++ 11 diz quenullptr
é uma palavra-chave , não um macro.C ++ não pode implementar
std::nullptr_t
, assim como C ++ não pode implementarint
ouvoid*
. Somente a implementação pode implementar essas coisas. É isso que o torna um "tipo fundamental"; faz parte da linguagem .Não há conversão implícita de uma constante de ponteiro nula para tipos integrais. Há uma conversão de
0
para um tipo integral, mas é porque é o número inteiro literal zero, que é ... um número inteiro.nullptr_t
pode ser convertido em um tipo inteiro (viareinterpret_cast
), mas só pode ser convertido implicitamente em ponteiros e embool
.fonte
std::nullptr_t
. Assim como você não pode escrever um tipo que seja exatamente equivalente ao comportamento exigido deint
. Você pode se aproximar, mas ainda haverá diferenças significativas. E não estou falando de detectores de características comois_class
essa que expõem que seu tipo é definido pelo usuário. Há coisas sobre o comportamento exigido de tipos fundamentais que você simplesmente não pode copiar usando as regras do idioma.nullptr_t
", você fala muito amplamente. E dizer "apenas a implementação pode implementá-lo" apenas confunde as coisas. O que você quer dizer é quenullptr_t
não pode ser implementado " na biblioteca C ++ porque faz parte da linguagem base.std::nullptr_t
é necessário. Assim como C ++, a linguagem não pode implementar um tipo que faz tudo o queint
é necessário.