21

以前は、この質問に対する答えは「100%」だと思っていましたが、最近、再考する価値のある例を指摘されました。自動保存期間を持つオブジェクトとして宣言された C 配列を考えてみましょう。

int main()
{
    int foo[42] = { 0 };
}

ここで、 の型fooは明らかにint[42]です。代わりに、次のケースを検討してください。

int main()
{
    int* foo = new int[rand() % 42];
    delete[] foo;
}

ここで、 の型はfooですが、コンパイル時にによって作成されたオブジェクトint*の型をどのように判断できますか? (強調は、式によって返されるポインターについて話しているのではなく、式によって作成された配列オブジェクトについて話しているという事実を強調することを目的としています)。newnewnew

newこれは、C++11 標準のパラグラフ 5.3.4/1 が式の結果について指定しているものです。

[...] new-expressionによって作成されたエンティティには、動的ストレージ期間 (3.7.4) があります。[ 注: そのようなエンティティの有効期間は、必ずしもそれが作成されたスコープに制限されるわけではありません。—終わりの注] エンティティが非配列オブジェクトの場合、new-expressionは作成されたオブジェクトへのポインターを返します。配列の場合、new-expression は配列の最初の要素へのポインターを返します。

私は以前、C++ ではすべてのオブジェクトの型がコンパイル時に決定されると考えていましたが、上記の例はその信念を反証しているようです。また、パラグラフ 1.8/1:

[...] オブジェクトのプロパティは、オブジェクトの作成時に決定されます。オブジェクトには名前を付けることができます (条項 3)。オブジェクトには、その寿命 (3.8) に影響を与える保存期間 (3.7) があります。オブジェクトには型 (3.9) があります。[...]

だから私の質問は:

  1. 最後に引用した段落の「プロパティ」は何を意味していますか? 明らかに、オブジェクトの名前は、「オブジェクトが作成されたときに」決定されるものとしてカウントすることはできません-ここで「作成された」が私が考えているものとは異なることを意味しない限り。
  2. 実行時にのみタイプが決定されるオブジェクトの例は他にありますか?
  3. C++ が静的に型付けされた言語であると言うのは、どの程度正しいのでしょうか? というか、この点で C++ を分類する最も適切な方法は何ですか?

誰かが上記の点の少なくとも 1 つについて詳しく説明できれば素晴らしいことです。

編集:

標準は、new式が実際に配列 objectを作成することを明確にしているようです。パラグラフ 5.3.4/5 による ( Xeo 提供):

割り当てられたオブジェクトが配列の場合(つまり、noptr-new-declarator構文が使用されるか、new-type-idまたは type-idが配列型を示す)、new-expression は最初の要素へのポインターを生成します ( any) の配列。[注: と の両方が typenew intを持ち、 の型は--end note ] noptr-new-declarator のattribute - specifier-seq、関連付けられた配列 type に属します。new int[10]int*new int[i][10]int (*)[10]

4

3 に答える 3

9

「静的型」および「動的型」という用語は、式に適用されます。

静的タイプ

実行セマンティクスを考慮せずにプログラムを分析した結果の式(3.9)の型。


動的タイプ

<glvalue> glvalue 式で示される glvalue が参照する最も派生したオブジェクト (1.8) の型

さらに、動的型は、静的型を派生できる場合にのみ静的型と異なることがわかります。これは、動的配列型が常に式の静的型と同じであることを意味します。

だからあなたの質問:

しかし、コンパイル時に new 式によって作成されたオブジェクトの型をどのように見分けることができるでしょうか?

オブジェクトには型がありますが、オブジェクトを参照する式がない「静的」または「動的」型ではありません。式を指定すると、静的型は常にコンパイル時に認識されます。派生がない場合、動的型は静的型と同じです。

しかし、式とは関係なく、オブジェクトの型について質問しています。あなたが与えた例では、オブジェクトを作成するように要求しましたが、コンパイル時に作成したいオブジェクトのタイプを指定していません。次のように見ることができます。

template<typename T>
T *create_array(size_t s) {
    switch(s) {
        case 1: return &(*new std::array<T, 1>)[0];
        case 2: return &(*new std::array<T, 2>)[0];
        // ...
    }
}

これには特別なことやユニークなことはほとんどありません。別の可能性は次のとおりです。

struct B { virtual ~B() {}};
struct D : B {};
struct E : B {};

B *create() {
    if (std::bernoulli_distribution(0.5)(std::default_random_engine())) {
        return new D;
    }
    return new E;
}

または:

void *create() {
    if (std::bernoulli_distribution(0.5)(std::default_random_engine())) {
        return reinterpret_cast<void*>(new int);
    }
    return reinterpret_cast<void*>(new float);
}

との唯一の違いnew int[]は、作成するさまざまなタイプのオブジェクトを選択するためにその実装を確認できないことです。

于 2013-04-11T18:27:21.543 に答える
9

new-expressionは、ランタイムによって変化する配列型のオブジェクトを作成しません。これは、それぞれ static タイプの多くのオブジェクトを作成しますint。これらのオブジェクトの数は、静的にはわかりません。


C++ では、動的型の 2 つのケース (セクション 5.2.8) が提供されています。

  • 式の静的型と同じ
  • 静的型が多態的である場合、最も派生したオブジェクトの実行時型

これらのどちらもnew int[N]、動的配列型によって作成されたオブジェクトを提供しません。


皮肉なことに、 new-expressionを評価すると、重複する配列オブジェクトが無数に作成されます。3.8p2 から:

[ 注: 配列オブジェクトの有効期間は、適切なサイズとアライメントを持つストレージが取得されるとすぐに開始され、配列が占有するストレージが再利用または解放されると、その有効期間は終了します。12.6.2 は、基本サブオブジェクトとメンバー サブオブジェクトの有効期間について説明しています。— エンドノート]

したがって、 によって作成された「配列オブジェクト」について話したい場合は、new int[5]型だけでなく、、、、およびもint[5]指定する必要があります。int[4]int[1]char[5*sizeof(int)]struct s { int x; }[5]

これは、実行時に配列型が存在しないと言うのと同じだと思います。オブジェクトのタイプは、限定的で情報的であり、そのプロパティについて何かを伝えるものであると想定されています。事実上、異なる型を持つ無限の重複する配列オブジェクトとしてメモリ領域を処理できるようにすることは、配列オブジェクトが完全に型のないことを意味します。ランタイム型の概念は、配列内に格納された要素オブジェクトに対してのみ意味があります。

于 2013-04-10T23:11:58.333 に答える
2

私は以前、C++ ではすべてのオブジェクトの型がコンパイル時に決定されると考えていましたが、上記の例はその信念を反証しているようです。

あなたが引用した例は、アイテムの保管期間について話している. C++ は、次の 3 つの保存期間を認識します。

  1. 静的ストレージ期間は、グローバルおよびローカルの静的変数の期間です。
  2. 自動ストレージ期間は、「スタックに割り当てられた」関数ローカル変数の期間です。
  3. 動的ストレージ期間は、newまたはなどで動的に割り当てられたメモリの期間ですmalloc

ここでの「動的」という言葉の使用は、オブジェクトのタイプとは何の関係もありません。これは、実装がオブジェクトを構成するデータを格納する方法を指します。

私は以前、C++ ではすべてのオブジェクトの型がコンパイル時に決定されると考えていましたが、上記の例はその信念を反証しているようです。

あなたの例では、 type を持つ1つの変数がありますint*。プログラムにとって意味のある方法で回復できる、基礎となる配列の実際の配列タイプはありません。動的な型付けは行われません。

于 2013-04-10T23:13:09.527 に答える