Je suis curieux de savoir comment ça nullptr
marche. Les normes N4659 et N4849 stipulent:
- il doit avoir du type
std::nullptr_t
; - vous ne pouvez pas prendre son adresse;
- il peut être directement converti en pointeur et pointeur en membre;
sizeof(std::nullptr_t) == sizeof(void*)
;- sa conversion en
bool
estfalse
; - sa valeur peut être convertie en type intégral à l'identique
(void*)0
, mais pas à l'envers;
C'est donc fondamentalement une constante avec la même signification que (void*)0
, mais elle a un type différent. J'ai trouvé l'implémentation de std::nullptr_t
sur mon appareil et c'est comme suit.
#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
Je suis cependant plus intéressé par la première partie. Il semble satisfaire les points 1-5, mais je n'ai aucune idée pourquoi il a une sous-classe __nat et tout ce qui s'y rapporte. Je voudrais également savoir pourquoi il échoue sur les conversions intégrales.
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
la source
la source
nullptr_t
est un type fondamental. Comment est-ilint
mis en œuvre?#ifdef _LIBCPP_HAS_NO_NULLPTR
. Cela semble être une solution de contournement lorsque le compilateur ne fournit pasnullptr
.nullptr_t
c'est un type fondamental. L'implémenter en tant que type de classe ne fait pas une implémentation conforme. Voir le commentaire de chris.is_class
etis_null_pointer
ne peut pas être vrai pour le même type. Une seule des fonctions de catégorie de type principal peut renvoyer true pour un type spécifique.Réponses:
Cela fonctionne de la manière la plus simple possible: par fiat . Cela fonctionne parce que la norme C ++ dit que cela fonctionne, et cela fonctionne comme il le fait parce que la norme C ++ dit que les implémentations doivent le faire fonctionner de cette façon.
Il est important de reconnaître qu'il est impossible d'implémenter en
std::nullptr_t
utilisant les règles du langage C ++. La conversion d'une constante de pointeur null de typestd::nullptr_t
en pointeur n'est pas une conversion définie par l'utilisateur. Cela signifie que vous pouvez passer d'une constante de pointeur nul à un pointeur, puis passer par une conversion définie par l'utilisateur vers un autre type, le tout dans une seule séquence de conversion implicite.Ce n'est pas possible si vous implémentez en
nullptr_t
tant que classe. Les opérateurs de conversion représentent des conversions définies par l'utilisateur, et les règles de séquence de conversion implicites de C ++ ne permettent pas plus d'une conversion définie par l'utilisateur dans une telle séquence.Le code que vous avez publié est donc une bonne approximation
std::nullptr_t
, mais ce n'est rien de plus que cela. Ce n'est pas une implémentation légitime du type. Ce fut probablement d'une ancienne version du compilateur ( à gauche pour des raisons de compatibilité ascendante) avant que le compilateur fourni un soutien approprié pourstd::nullptr_t
. Vous pouvez voir cela par le fait que c'est#define
snullptr
, tandis que C ++ 11 dit quenullptr
c'est un mot - clé , pas une macro.C ++ ne peut pas implémenter
std::nullptr_t
, tout comme C ++ ne peut pas implémenterint
ouvoid*
. Seule l'implémentation peut implémenter ces choses. C'est ce qui en fait un "type fondamental"; cela fait partie de la langue .Il y a pas de conversion implicite d'une constante de pointeur nul en types intégraux. Il y a une conversion de
0
à un type intégral, mais c'est parce que c'est le zéro littéral entier, qui est ... un entier.nullptr_t
peut être jeté à un type entier (viareinterpret_cast
), mais elle ne peut être converti implicitement à des pointeurs etbool
.la source
std::nullptr_t
. Tout comme vous ne pouvez pas écrire un type exactement équivalent au comportement requis deint
. Vous pouvez vous rapprocher, mais il y aura toujours des différences importantes. Et je ne parle pas de détecteurs de traits comme ceux-ciis_class
qui montrent que votre type est défini par l'utilisateur. Il y a des choses sur le comportement requis des types fondamentaux que vous ne pouvez tout simplement pas copier en utilisant les règles de la langue.nullptr_t
", vous parlez trop largement. Et dire "seule l'implémentation peut l'implémenter" ne fait que confondre les choses. Ce que vous voulez dire, c'est quenullptr_t
cela ne peut pas être implémenté " dans la bibliothèque C ++ car il fait partie du langage de base.std::nullptr_t
est nécessaire pour le faire. Tout comme C ++, le langage ne peut pas implémenter un type qui fait tout ce quiint
est nécessaire pour le faire.