Where is nullptr_t located?

A bit of background.

I wrote a game engine for a long time. It is divided into several static libraries, such as "utils", "rsbin" (resource system), "window", which are then linked into a single executable file.

This is a cross-platform engine compiled for Windows and Android. On Windows, I compile it using MinGW. On Android, with CCTools, which is the interface for the native gcc.

One of the base classes is utils::RefObject , which is a concept similar to Windows IUnknown: it provides a reference counter to determine its lifetime and a method for requesting a specific interface from the base class pointer. There is also template< typename T > utils::Ref , designed specifically for such objects. It contains std::atomic< utils::RefObject* > and automatically updates its std::atomic< utils::RefObject* > object when building, assigning and destroying, similar to std::shared_ptr . It also allows you to implicitly convert RefObjects of various types using its query methods. Although, it is inefficient to query an object for its own type, therefore utils::Ref overloads most of its operators, e. d. there is a concrete constructor utils::Ref< T >::Ref( T* ptr ) , which simply increases the conversion factor of the passed object and the general utils::Ref< T >::Ref( RefObject* ptr ) , which asks for its argument for example, T throws an exception on failure (do not worry, although, of course, there is a method for soft cast).

But having only these two methods creates a problem: you cannot explicitly initialize utils::Ref null pointer, since it is ambiguous; therefore there is also utils::Ref< T >::Ref( nullptr_t ) to provide a way to do this.

Now we are facing a problem. In the header file, the prototype is written in exactly the same way as above, without the previous std:: . Please note that I do not use using namespace . It worked for a long time.

Now I am working on a graphics system. It existed before, but it was rather rudimentary, so I did not even notice that <gl.h> actually defines only OpenGL 1.1, while for newer versions you should go through <glext.h>. Now it has become necessary to use the latter. But also he broke the old reference class.

Judging by the error messages, MinGW now has problems with this nullptr_t in prototypes. I did a quick search on the Internet and found that it is often called std::nullptr_t . Although not everywhere.

Quick conclusion: I had nullptr_t without std:: or using namespace compilation until I included <glext.h> before the header.

The site I have used so far, cplusplus.com/reference, suggests that the global ::nullptr_t should be exactly the same . On the other hand, the en.cppreference.com wiki reports that std::nullptr_t .

The quick test program, helloworld with void foo( int ) and void foo( nullptr_t ) , could not be compiled, and the reason now is clearly "error: 'nullptr_t' was not declared in this scope" with a suggestion to use std::nullptr_t instead.

There is no need to add std:: where necessary; but this incident left me quite curious.

cplusplus.com actually lay? => Answered in commets, yes. This is an inaccurate source.

Then, if nullptr_t really in namespace std , why does utils::Ref compile? => With suggestions in the comments, I ran a couple of tests and found that <mutex> included in some other heading, when placed before any heading, stddef defines global ::nullptr_t . Of course, this is not ideal behavior, but it is not a serious mistake. Probably should report this to the MinGW / GCC developers.

Why does the inclusion of <glext.h> break it? => When the stddef header is included before <mutex>, the type is defined as std::nullptr_t according to the standard. <glext.h> includes <windows.h>, which, in turn, certainly includes the stddef header, among the entire package of others that are necessary for WinAPI.

Here are the sources defining the class in question:

(the last 2 are included and may also affect)

As suggested in the comments, I ran g ++ -E in a test case, which compiled, and found a rather interesting bit in <stddef.h>:

 #if defined(__cplusplus) && __cplusplus >= 201103L #ifndef _GXX_NULLPTR_T #define _GXX_NULLPTR_T typedef decltype(nullptr) nullptr_t; #endif #endif /* C++11. */ 

Now, to find where _GXX_NULLPTR_T defined different ... quick GREP through MinGW files did not find anything other than this stddef.h

So, it’s still a mystery why and how she becomes disabled. Especially if you include only <stddef.h> and nothing else defines nullptr_t anywhere, despite the bit above.

+6
source share
1 answer

The nullptr type nullptr defined in the ::std , so the correct qualification is ::std::nullptr_t . Of course, this means that you usually wrote it std::nullptr_t in practice.

Quote C ++ 11:

2.14.7 / 1:

The pointer literal is the nullptr keyword. This is a prvalue of type std::nullptr_t .

18.2 / 9:

nullptr_t defined as follows:

 namespace std { typedef decltype(nullptr) nullptr_t; } 

A type for which nullptr_t is a synonym has the characteristics described in clauses 3.9.1 and 4.10. [Note: Although the address of nullptr s cannot be accepted, the address of another nullptr_t object, which is the value of l, can. -end note]

<stddef.h> also included in the picture. 18.2 talks about <cstddef> , so the C ++ header, where std::nullptr_t defined. Per D.5 / 2:

Each C header, each of which has a name of the form name.h , behaves as if each name was placed in the standard library namespace with the corresponding header cname placed in the global namespace.

This means that including <stddef.h> gives you access to ::nullptr_t . But since this should be the C title, I would advise against relying on it in C ++ code (even if it is formally correct).

+5
source

Source: https://habr.com/ru/post/982941/


All Articles