Немного предыстории.Где находится nullptr_t?
Я долгое время писал игровой движок. Он разделен на несколько статических библиотек, таких как «utils», «rsbin» (ресурсная система), «окно», которые затем связаны в один исполняемый файл.
Это кроссплатформенный движок, скомпилированный для Windows и Android. Под Windows я скомпилирую его с помощью MinGW. Под Android, с CCTools, который является интерфейсом для родного gcc.
Один из базовых классов: utils::RefObject
, который представляет собой концепцию, похожую на IUnknown в Windows: она предоставляет ссылочный счетчик для определения его времени жизни и метода для запроса конкретного интерфейса из указателя базового класса. Также есть template< typename T > utils::Ref
, специально предназначенный для такого рода объектов. Он содержит std::atomic< utils::RefObject* >
и автоматически обновляет его стоимость при строительстве, назначении и уничтожении, аналогично std::shared_ptr
. Он также позволяет неявно преобразовывать RefObjects разных типов с помощью своих методов запросов. Хотя, неэффективно запрашивать объект для его собственного типа, поэтому utils::Ref
перегружает большинство своих операторов, e. г. существует конкретный конструктор utils::Ref<T>::Ref(T* ptr)
, который просто увеличивает коэффициент пересчета прошедшего объекта и общий utils::Ref<T>::Ref(RefObject* ptr)
, который запрашивает свой аргумент, например, из T, и выдает исключение при сбое (не волнуйтесь, хотя, конечно, есть метод для мягкого литья).
Но только с этими двумя методами возникает проблема: вы не можете явно инициализировать utils::Ref
нулевым указателем, поскольку он неоднозначен; поэтому есть также utils::Ref<T>::Ref(nullptr_t)
, чтобы предоставить способ сделать это.
Теперь мы сталкиваемся с проблемой. В файле заголовка прототип записывается точно так же, как указано выше, без предшествующего std::
. Заметьте, что я тоже не использую using namespace
. Долгое время это сработало.
Теперь я работаю над графической системой. Он существовал раньше, но он был довольно рудиментарным, поэтому я даже не заметил, что <gl.h> фактически определяет только OpenGL 1.1, в то время как для более новых версий вы должны пройти <glext.h>. Теперь стало необходимо использовать последнее. Но в том числе он сломал старый ссылочный класс.
Судя по сообщениям об ошибках, MinGW теперь имеет проблемы с этим nullptr_t
в прототипах. Я сделал быстрый поиск в Интернете и обнаружил, что часто это std::nullptr_t
. Хотя, не везде.
Быстрый sumup: Я был nullptr_t
без любой std::
или using namespace
составителя хорошо, пока я не включены <glext.h> перед заголовком.
Сайт, который я использовал до сих пор, cplusplus.com/reference, предлагает, что глобальный ::nullptr_t
is exactly how it should be. С другой стороны, en.cppreference.com wiki tells that это на самом деле std::nullptr_t
.
Быстрая тестовая программа, HelloWorld с void foo(int)
и void foo(nullptr_t)
, не удалось собрать и почему-то теперь явно "error: 'nullptr_t' was not declared in this scope"
с предложением использовать std::nullptr_t
вместо.
Нет необходимости добавлять std::
, если необходимо; но этот случай оставил мне довольно любопытным.
cplusplus.com на самом деле лежал? => Отвечено в commets, да. Это неточный источник.
Тогда, если nullptr_t
действительно находится в namespace std
, почему utils::Ref
скомпилировать? => С предложениями в комментариях провела пару тестов и обнаружила, что <mutex>, включенный в некоторый другой заголовок, при размещении перед любым заголовком stddef определяет глобальный ::nullptr_t
. Конечно, это не идеальное поведение, но это не главная ошибка. Вероятно, он должен сообщать об этом разработчикам MinGW/GCC.
Почему включение <glext.h> не работает? => Когда заголовок stddef включен до <mutex>, тип определяется в соответствии со стандартом, как std::nullptr_t
. <glext.h> включает в себя <windows.h>, который, в свою очередь, обязательно содержит заголовок stddef, среди всего пакета других, которые необходимы для WinAPI.
Вот источники, определяющие класс в вопросе:
- utils/ref.hpp
- utils/ref.cpp
- utils/refobject.hpp
- utils/refobject.cpp
- utils/logger.hpp => Это один использует мьютекс, чтобы избежать линии разрывов во время вывод.
- utils/cbase.hpp
(последние 2 включены, и поэтому могут повлиять тоже)
Как было предложено в комментариях, я побежал г ++ -E на тестовом примере, который компилируется и нашел довольно интересный бит в <stddef.h>:
#if defined(__cplusplus) && __cplusplus >= 201103L
#ifndef _GXX_NULLPTR_T
#define _GXX_NULLPTR_T
typedef decltype(nullptr) nullptr_t;
#endif
#endif /* C++11. */
Теперь чтобы найти, где _GXX_NULLPTR_T
определяется еще ... быстро GREP через файлы MinGW не нашла ничего, кроме этого stddef.h
Итак, все еще остается загадкой, почему и как он становится инвалидом. Особенно при включении только <stddef.h> и ничего больше не определяет nullptr_t
где угодно, несмотря на бит выше.
Посмотрите на предварительно обработанный вывод, чтобы увидеть, где вводится ':: nullptr_t'. – tenfour
FWIW, cplusplus.com известен как неточный. – Angew
Он относится к std, ненулевым коэффициентам, что этот исходный код увидел еще один компилятор, который использовал 'using :: std :: nullptr_t;' в заголовке. Как MSVC++. –