-1

C++ 関数テンプレートを学習しようとしています。関数テンプレートへのポインターとして配列を渡しています。その中で、配列のサイズを見つけようとしています。これが私が使用する関数テンプレートです。

template<typename T>
T* average( T *arr)
{
    T *ansPtr,ans,sum = 0.0;    

    size_t sz = sizeof(arr)/sizeof(arr[0]);
    cout<<"\nSz is "<<sz<<endl;

    for(int i = 0;i < sz; i++)
    {
        sum = sum + arr[i];
    }
    ans = (sum/sz);
    ansPtr = &ans;
    return ansPtr;
}

このcoutステートメントは、ポインターを整数の配列に渡している場合でも、のサイズarrを表示します。これは、以前に言及した質問と重複している可能性があることはわかっていますが、これについてのより良い説明が必要です.15

私が思いついた唯一のことは、テンプレートは実行時に呼び出されsizeof、コンパイル時の演算子であるため、コンパイラはその行を無視することです

   int sz = sizeof(arr)/sizeof(arr[0]);

実際に関数を呼び出すまで、arr の正確なタイプがわからないためです。それは正しいですか、それともここで何かが欠けていますか? また、配列へのポインターを関数テンプレートに送信することは信頼できますか?

4

4 に答える 4

6
 T *arr

arr これは、" が "へのポインターであるため、C++ ですT。明らかな理由から、「配列のサイズ」ではなく「sizeof(arr)ポインターのサイズ」を意味することは明らかです。それがその計画の重大な欠陥です。arrarr

配列のサイズを取得するには、関数は配列に対して操作する必要がありますが、明らかにポインターに対して操作する必要はありません。誰もが知っているように (正しい?)、配列はポインターではありません。

さらに、平均関数は平均値を返す必要があります。しかしT*、「へのポインタT」です。平均関数は、値へのポインターを返すべきではありません。それは値ではありません。

ポインターの戻り値の型を持つことは、最後の違反ではありません。ローカル変数へのポインターを返すことは、何よりも最悪ですなぜホテルの部屋の鍵を盗もうとするのですか?

template<typename T, std::size_t sz>
T average( T(&arr)[sz])
{
    T ans,sum = 0.0;    

    cout<<"\nSz is "<<sz<<endl;

    for(int i = 0;i < sz; i++)
    {
        sum = sum + arr[i];
    }
    ans = (sum/sz);
    return ans;
}
于 2012-06-28T06:51:03.030 に答える
4

渡されたパラメータのサイズにアクセスできるようにしたい場合は、それもテンプレート パラメータにする必要があります。

template<typename T, size_t Len>
T average(const T (&arr)[Len])
{
    T sum = T();
    cout<<"\nSz is "<<Len<<endl;
    for(int i = 0;i < Len; i++)
    {
        sum = sum + arr[i];
    }
    return (sum/Len);
}

明らかに、sizeof を省略できます。また、動的に割り当てられた配列を誤って渡すことはありません。これは良いことです。欠点として、テンプレートはすべてのタイプに対して 1 回だけでなく、すべてのサイズに対して 1 回インスタンス化されます。コードの大部分の重複を避けたい場合は、ポインターと長さを受け入れて平均を返す 2 番目のテンプレート化された関数を使用できます。これは、インライン関数から呼び出される可能性があります。

template<typename T>
T average(const T* arr, size_t len)
{
    T sum = T();
    cout<<"\nSz is "<<len<<endl;
    for(int i = 0;i < len; i++)
    {
        sum = sum + arr[i];
    }
    return (sum/len);
}

template<typename T, size_t Len>
inline T average(const T (&arr)[Len])
{
    return average(arr, Len);
}

また、関数に対してローカルな変数のアドレスを返すことは、関数よりも長く存続しないため、非常に悪い考えであることに注意してください。したがって、値を返し、不要なコピーを最適化してコンパイラに任せる方がよいでしょう。

于 2012-06-28T06:56:13.370 に答える
3

配列はパラメーターとして渡されるとポインターに減衰するため、実質的にポインターのサイズを取得しています。テンプレートとは関係ありません。言語がどのように設計されているかです。

于 2012-06-28T06:48:27.060 に答える
1

他の人は直接の誤りを指摘していますが、私見ですが、彼らが対処していない重要な点が 2 つあります。どちらも、製品コードで発生した場合はエラーと見なします。

まず、なぜ使わないのですstd::vectorか?歴史的な理由から、C スタイルの配列は壊れており、通常は避けるべきです。例外もありますが、ほとんどの場合、静的変数の静的初期化が含まれます。C スタイルの配列を関数の引数として渡してはいけません。これは、遭遇したような問題を引き起こすためです。(C スタイルの配列 std::vector効率の両方を処理できる関数を作成することは可能です。ただし、関数はテンプレート型の 2 つのイテレータを取る関数テンプレートである必要があります。)

2 つ目は、なぜ標準ライブラリの関数を使用しないのですか? 関数は基本的に 1 行で記述できます。

template <typename ForwardIterator>
typename ForwardIterator::value_type
average( ForwardIterator begin, ForwardIterator end )
{
    return std::accumulate( begin, end, 
                            typename::ForwardIterator::value_type() )
                / std::distance( begin, end );
}

(もちろん、この関数は、丸め誤差によって結果が価値のないものになる可能性がある浮動小数点型に対しては信頼できません。浮動小数点は、一連の追加の問題を引き起こします。また、おそらく整数型に対しても信頼性が低いと思われます。オーバーフローの危険性. しかし、これらはより高度な問題です.)

于 2012-06-28T07:41:18.510 に答える