6

少し前史。

私はかなり長い間ゲームエンジンを書いてきました。これは、「utils」、「rsbin」(リソース システム)、「window」などのいくつかの静的ライブラリに分割され、単一の実行可能ファイルにリンクされます。

これはクロスプラットフォーム エンジンであり、Windows および Android 用にコンパイルされています。Windows では、MinGW でコンパイルします。Android では、ネイティブ gcc へのインターフェイスである CCTools を使用します。

基本クラスの 1 つは、utils::RefObjectWindows の IUnknown に似た概念を表す です。これは、その有効期間を決定するための参照カウンターと、基本クラス ポインターから特定のインターフェイスを照会するためのメソッドを提供します。template< typename T > utils::Refこの種のオブジェクト用に特別に設計されたもあります。を保持し、std::atomic< utils::RefObject* >と同様に、構築、割り当て、および破棄時にオブジェクトの参照カウントを自動的に更新しますstd::shared_ptr。また、クエリ メソッドを使用して、さまざまなタイプの RefObject を暗黙的に変換することもできます。ただし、オブジェクトに独自の型を照会するのは非効率的であるため、utils::Refその演算子のほとんどをオーバーロードします。たとえばutils::Ref< T >::Ref( T* ptr )、渡されたオブジェクトの参照カウントを単純にインクリメントする特定のコンストラクターがあり、一般的なutils::Ref< T >::Ref( RefObject* ptr )これは、T のインスタンスの引数をクエリし、失敗すると例外をスローします (もちろん、ソフト キャストのメソッドはありますが、心配する必要はありません)。

utils::Refしかし、これら 2 つのメソッドだけを使用すると問題が発生します。null ポインターはあいまいであるため、明示的に初期化することはできません。utils::Ref< T >::Ref( nullptr_t )そのため、それを行う方法も提供する必要があります。

さて、当面の問題に取り掛かります。ヘッダー ファイルでは、prototype のスペルは上記のとおりで、先頭にstd::. どちらも使用しないことに注意してくださいusing namespace。長い間、これはうまくいきました。

今、私はグラフィックシステムに取り組んでいます。以前から存在していましたが、かなり初歩的なものだったので、<gl.h> が実際に OpenGL 1.1 しか定義していないことに気づきませんでしたが、新しいバージョンでは <glext.h> を使用する必要があります。今、後者を使用することが必要になりました。しかし、それを含めると、古い参照クラスが壊れました。

エラー メッセージから判断すると、MinGW は現在nullptr_t、プロトタイプで問題を抱えています。Web で簡単に検索したところ、と呼ばれることが多いstd::nullptr_tことがわかりました。ただし、どこでもではありません。

簡単なまとめ:ヘッダーの前に <glext.h> をインクルードするまでnullptr_t、どちらstd::もコンパイルせずに問題なくコンパイルできました。using namespace

私がこれまで使ってきたサイト cplusplus.com/reference は、global::nullptr_t がまさにあるべき姿であることを示唆しています。一方、en.cppreference.com wikiでは、実際にはstd::nullptr_t.

void foo( int )とを使用した helloworld の簡単なテスト プログラムvoid foo( nullptr_t )がコンパイルに失敗し、その理由が明確になり、代わり"error: 'nullptr_t' was not declared in this scope"に使用するよう提案されました。std::nullptr_t

std::必要な場所に追加するのは難しくありません。しかし、この事件は私にかなり興味をそそられました。

cplusplus.com は実際に嘘をついていたのですか? =>コメントで答えました、はい。不正確なソースです。

では、nullptr_t実際に に存在する場合namespace std、なぜutils::Refコンパイルしたのでしょうか? => コメントの提案により、いくつかのテストを実行し、他のヘッダーに含まれる <mutex> が stddef ヘッダーの前に配置されると、 global を定義することを発見しました::nullptr_t。確かに理想的な動作ではありませんが、大きなバグではありません。いずれにせよ、おそらく MinGW/GCC 開発者に報告する必要があります。

<gext.h> を含めるとなぜ壊れるのですか? => <mutex> の前に stddef ヘッダーが含まれている場合、型は標準に従って として定義されstd::nullptr_tます。<glext.h> には <windows.h> が含まれています。これには、WinAPI に必要なその他のパック全体とともに、stddef ヘッダーが確実に含まれています。

問題のクラスを定義するソースは次のとおりです。

(後者の2つは含まれているため、影響を受ける可能性もあります)

コメントで示唆されているように、コンパイルされたテスト ケースで g++ -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... MinGWのファイルをすばやくGREPすると、このstddef.h以外に何も見つかりませんでした

そのため、なぜ、どのように無効になっているのかはまだ謎です。特に <stddef.h> だけを含め、他に何も定義していない場合はnullptr_t、上記のビットにもかかわらず、どこにも定義されていません。

4

1 に答える 1

5

の型はnullptrnamespace::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できます。—終わりのメモ]

<stddef.h>も絵に入ります。18.2 は について話しているので、それは が定義され<cstddef>ている C++ ヘッダーです。std::nullptr_tD.5/2によると:

それぞれが の形式の名前を持つすべての C ヘッダーはname.h、対応するヘッダーによって標準ライブラリ名前空間に配置された各名前cnameがグローバル名前空間スコープ内に配置されているかのように動作します。

つまり、インクルードすると<stddef.h>にアクセスできるようになり::nullptr_tます。しかし、これは C ヘッダーであるはずなので、C++ コードでこれに依存することはお勧めしません (たとえそれが正式に有効であっても)。

于 2015-02-24T13:08:28.927 に答える