web-dev-qa-db-ja.com

`sizeof`は*本当に*` std :: size_t`に評価されますか?それをできる?

次の標準的なパッセージを取ります。

[C++11: 5.3.3/6]:sizeofsizeof...の結果は、タイプstd::size_tの定数です。 [注:std::size_tは、標準ヘッダー<cstddef>(18.2)で定義されています。 -文末脚注]

今:

[C++11: 18.2/6]:size_tは、実装で定義された符号なし整数型であり、任意のオブジェクトのバイト単位のサイズを含めるのに十分な大きさです。

確かに、このパッセージではsize_ttypedefで定義された型エイリアスである必要はありませんが、標準ヘッダー<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つに名前を付けるだけなので、実際には問題はありません。

52
Dietmar Kühl

はい。

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 ...は定数;ではありません定数。結果を「一定の値」と呼ぶのは合理的でした。)

5
Keith Thompson

私が理解しているように、この標準的なパッセージには次の表現が必要です。

_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)
{}
_
5
rodrigo

同じタイプですが、使用するにはそのヘッダーを含める必要があります。

2
user2030677