2015-02-24 3 views
6

Немного предыстории.Где находится 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_tis 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.

Вот источники, определяющие класс в вопросе:

(последние 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 где угодно, несмотря на бит выше.

+0

Посмотрите на предварительно обработанный вывод, чтобы увидеть, где вводится ':: nullptr_t'. – tenfour

+3

FWIW, cplusplus.com известен как неточный. – Angew

+0

Он относится к std, ненулевым коэффициентам, что этот исходный код увидел еще один компилятор, который использовал 'using :: std :: nullptr_t;' в заголовке. Как MSVC++. –

ответ

5

Тип nullptr определен в пространстве имен ::std, поэтому правильная квалификация ::std::nullptr_t. Конечно, это означает, что вы обычно накладываете это на std::nullptr_t.

Цитирование C++ 11:

2.14.7/1:

Указатель буквальным является ключевым словом nullptr. Это prvalue типа std::nullptr_t.

18,2/9:

nullptr_t определяется следующим образом:

namespace std { 
    typedef decltype(nullptr) nullptr_t; 
} 

тип, для которого nullptr_t является синонимом имеет характеристики, описанные в разделе 3.9.1 и 4.10. [Примечание: . Адрес nullptr не может быть доставлен, адрес другого nullptr_t объекта, который может быть присвоен номером . -end примечание]

<stddef.h> также входит в картину. 18.2 говорит о <cstddef>, так что это заголовок C++, где определен std::nullptr_t. Пер D.5/2:

Каждые C заголовок, каждый из которых имеет имя формы name.h, ведет себя так, как будто каждое имя помещается в стандартных библиотекой пространств имен с помощью соответствующих cname заголовка помещается в пределах глобального пространство имен.

Это означает, что в том числе <stddef.h> дает вам доступ к ::nullptr_t. Но поскольку это должно быть заголовок C, я бы посоветовал не полагаться на это в коде на C++ (даже если он формально действителен).

+0

IMO не имеет смысла говорить, что '' nullptr' имеет тип 'nullptr_t'', а не' 'nullptr_t' обозначает тип' nullptr'. nullptr_t - это просто typedef. – Columbo

+1

Учитывая, что упомянутый OP теперь «», вероятно, стоит упомянуть, что этот заголовок определяет ':: nullptr_t' точно так же, как и' 'определяет' :: std :: nullptr_t'. – hvd

+0

@Columbo Я считаю, что существует DR, что определение в стандарте является круговым. – Angew

Смежные вопросы