0

C++ メタプログラミングの型推定についてちょっとした質問があります。何らかのアクションを行う特定の機能があります。

main.cpp

template<typename T> void foo(T arg) {
    // do some action on argument
    std::cout << typeid(arg).name() << std::endl;
}


int main(int argc, char** argv) {
    int array[100] = {0};
    std::cout << typeid(array).name() << std::endl;
    foo(array);

    return 0;
}

出力:

A100_i
Pi     

関数foo()のargが関数main()の配列とは別のデータ型を持っているのはなぜですか?

4

2 に答える 2

6

実際、配列を関数に渡すと、ポインター型に減衰します。ではなく でTあると推定されます。int*int[100]

減衰を防ぎたい場合は、パラメーターを参照で受け入れます。

template<typename T> void foo(T & arg) //Note `&` here!
{
  // do some action on argument
   std::cout << (typeid(arg).name() << std::endl;
}

これで、期待どおりに出力されますA100_iこのオンライン デモを参照してください。


質問:値を渡すと配列がポインター型に変わるのはなぜですか?

回答: C++ では配列 (および関数)をで渡すことができないためです。言語はそれを許可しません。代わりに、言語は、関数の引数として渡されるときにポインター型に分解する必要があります。減衰を防ぐために、それらをreferenceとして渡す必要があります。

于 2013-02-26T09:44:47.170 に答える
3

C スタイルの配列が壊れているためです。特に、C スタイルの配列型の関数引数を持つことはできません。関数を書く場合 (テンプレートのことは今のところ忘れます):

void foo( int arg[100] );

この言語では、コンパイラがこれを次のように処理する必要があります。

void foo( int* arg );

(そして、100は単なるコメントであり、コンパイラによって無視されます)。

テンプレートの場合にこれをサポートするために、コンパイラが非参照テンプレート引数と一致させようとすると、配列引数がポインターに変換され、型推定は配列型ではなくポインター型になります。

mainその結果、C スタイルの配列を期待する関数 (テンプレートまたはその他) を記述してはいけません (選択肢のないの 2 番目の引数を除きます)。

この破損は C との互換性のためにのみ存在するため、参照が含まれる場合、C++ はこれに従いません。そう:

template < typename T, size_t N >
void foo( T (&arg)[ N ] );

どちらの場合でも同じ結果が得られるはずです。関数が C スタイルの配列と他のもの (std::vector など) の両方で呼び出される可能性があると思われる場合は、両方に対してオーバーロードできます。上記のバージョンはより特化されており、可能であればより一般的なバージョンよりも優先されます。

より良い解決策は、C スタイルの配列を完全に避けることですが、初期化を伴う静的変数には便利です。コンパイラに要素数をカウントさせ、初期化子リストに従って配列のサイズを定義させることができるのは、C スタイルの配列のみです。そして、静的初期化を行います。std::vector実行時に初期化子をカウントしますが、静的変数として使用すると、初期化の順序の問題が発生する可能性があります。C スタイルの配列と

于 2013-02-26T10:04:32.153 に答える