次の標準的なパッセージを取ります。
[C++11: 5.3.3/6]:
sizeof
とsizeof...
の結果は、タイプstd::size_t
の定数です。 [注:std::size_t
は、標準ヘッダー<cstddef>
(18.2)で定義されています。 -文末脚注]
今:
[C++11: 18.2/6]:
型size_t
は、実装で定義された符号なし整数型であり、任意のオブジェクトのバイト単位のサイズを含めるのに十分な大きさです。
確かに、このパッセージではsize_t
がtypedef
で定義された型エイリアスである必要はありませんが、標準ヘッダー<cstddef>
で利用可能になることが明示されているため、<cstddef>
を含めないことを読んでください。 size_t
がプログラムで利用可能であるという保証を削除する必要があります。
ただし、その最初の引用によれば、タイプstd::size_t
の式を取得することはできます。
int main()
{
typedef decltype(sizeof(0)) my_size_t;
my_size_t x = 0; // OK
std::size_t y = 1; // error: 'size_t' is not a member of 'std'
}
std::size_t
はプログラムに表示されませんが、sizeof(0)
はそれでも表示されますか?本当に?
したがって、5.3.3/6
が欠陥があると言って、実際には「std::size_t
が解決するものと同じタイプ」であると言うのは正しくありませんが、 notstd::size_t
自体?
確かに、std::size_t
がタイプエイリアスである場合、2つは同じですが、これも実際には必要ありません。
領土の地図を混同しないでください。
タイプはタイプ名で名前を付けることができます。これらのタイプ名は、組み込み、ユーザー定義タイプ、またはtemplate
パラメーターであり、インスタンス化に応じて複数の異なるタイプを参照することもできます。
しかし、名前はタイプではありません。明らかに、標準ではすべての型に名前を付ける必要はありません。従来の_struct {}
_は名前のない型です。
_std::size_t
_は型名です。 sizeof(expression)
が返す型に名前を付けます。
コンパイラーは、型の正規名を持つことができます--___size_t
_は、固有の組み込みの正規型名を持つための1つの方法です。
標準では、sizeof(expression)
のタイプが何であれ、_#include <cstddef>
_を実行すると、名前_std::size_t
_がそのタイプを参照することがその節で保証されます。
標準では、タイプを名前で参照します。彼らは「このtypenameが参照するタイプ」とは言わず、単に「タイプ$ NAME $」と言います。コンパイラは、必要に応じてint
が___int_32_fast
_の別名であると判断でき、標準にも異論はありません。
これと同じことが_std::nullptr_t
_と_std::initializer_list<Ts>
_と_std::type_info
_でも起こります。これらのタイプの変数を使用する場合、必ずしもこれらのタイプの名前を提供するヘッダーをプログラムに含める必要はありません。 。
従来のC/C++組み込み型はすべて、ヘッダーを必要としない正規の名前を持っていました。欠点は、グローバルスコープ内の新しいタイプ名が他の識別子と衝突するため、これにより既存のコードが破損することです。
ヘッダーファイルをインクルードすることで名前を取得できる「名前のない型」を用意することで、この問題を回避します。
この標準では、sizeof(expr)
のタイプが_std::size_t
_と同じタイプであることが義務付けられています。 sizeof(expr)
を使用して名前_std::size_t
_を使用可能にする義務はありません。また、_std::size_t
_は組み込み整数型の1つに名前を付けるだけなので、実際には問題はありません。
はい。
sizeof
によって生成される型は、符号なし整数型です。実装はそれがどれであるかを定義します。
たとえば、特定の実装では、sizeof
式のタイプはunsigned long
の場合があります。
std::size_t
は、それがtypedef
の場合、unsigned long
の代替名にすぎません。したがって、これらの2つのステートメント:
sizeof ...
の型は、unsigned long
型の定数です。
そして
sizeof ...
の型は、std::size_t
型の定数です。
まったく同じことを言っていますその実装の場合。タイプunsigned long
とタイプstd::size_t
は同じタイプです。違いは、後者はすべての(準拠する)実装で正確であり、std::size_t
はたとえばunsigned int
またはその他の符号なしタイプのエイリアスである可能性があることです。
コンパイラーに関する限り、sizeof
はタイプunsigned long
の結果を生成します。コンパイラ(ランタイムライブラリではなく)は、size_t
という名前を知っている必要はありません。
これはすべて、std::size_t
(またはCについて話している場合は単にsize_t
)がtypedefであることを前提としています。これは、CまたはC++標準のどちらにも詳しく説明されていません。それでも、実装はsize_t
をtypedefにすることで、標準の要件に直接準拠できます。これらの要件を満たす方法は他にないと思いますポータブル。 (ユーザーの名前空間を侵害するため、マクロまたは実装定義のキーワードにすることはできません。また、マクロはstd
名前空間内でスコープされません。)コンパイラー可能性があります) make size_t
typedef以外の実装固有の構造ですが、typedefは完全に機能するため、そうしても意味がありません。標準でsize_t
がtypedefであると述べられていれば、私見です。
(無関係ですが、本当の問題は、標準が結果を「定数」と呼んでいることです。ISOCでは、「定数」は整数リテラルなどのトークンです。私が知る限り、C++はそうではありません。 t名詞「定数」を定義しますが、これは用語のISOC定義を参照します。sizeof ...
は定数式;ではありません定数。結果を「一定の値」と呼ぶのは合理的でした。)
私が理解しているように、この標準的なパッセージには次の表現が必要です。
_typeid(sizeof(0)) == typeid(std::size_t)
_
常にtrue
を生成します。実際の識別子_std::size_t
_、_::size_t
_、またはその他のエイリアス/ typedefを使用する場合、std::typeinfo::operator==()
に従ってタイプのIDが保持されている限り、関係ありません。
同じタイプアイデンティティ問題は言語の他の場所でも発生します。たとえば、私の64ビットマシンでは、関数の再定義が原因で次のコードのコンパイルに失敗します。
_#include <cstddef>
void foo(std::size_t x)
{}
void foo(unsigned long x)
{}
_
同じタイプですが、使用するにはそのヘッダーを含める必要があります。