5
#include <iostream>

template <int M, int N>
void print1(int src[M][N]) { }

void print2(int src[4][4]) { }

int main() {

    int src[][4] = {
        { 1,  2,  3,  4},
        { 5,  6,  7,  8},
        { 9, 10, 11, 12},
        {13, 14, 15, 16},
    };

    print1(src);
    // gives error
    // error: no matching function for call to 'print1(int [4][4])'

    print2(src);
    // works!
}

上記のコードでprint2()は、期待どおりに機能しprint1()ますが、エラーが発生します

エラー:「print(int [4] [4])」の呼び出しに一致する関数がありません

わかりません。まったく同じように見えます。ハードコードされた値を置き換えてテンプレートを使用し、任意のサイズの配列を受け入れることができるようにしました。

なぜ機能しないのですか?私は何が間違っているのですか?

4

7 に答える 7

11

宣言で

void print2(int src[4][4])

最初4は無意味です。この関数は、次のように宣言した場合と同じです。

void print2(int src[][4])

またはとして

void print2(int (*src)[4])

これは、CおよびC++では配列が値によって渡されることがないためです。むしろ、配列が関数に渡されると、その配列は暗黙的にその初期要素へのポインターに変換されます。同様に、関数パラメータの型が「配列T」の場合、「ポインタ型」型に自動的に変換されますT。実際、CおよびC++には配列型パラメーターはありません。

それでは、関数テンプレートについて考えてみましょう。

template <int M, int N>
void print1(int src[M][N])

と同様にprint2、この関数テンプレートは次と同等です。

template <int M, int N>
void print1(int src[][N])

呼び出しでテンプレート引数を明示的に指定せずにこの関数を呼び出すには、コンパイラーが引数の型から何を推測できる必要がありMますN引数リストのどこにも実際Mには表示されないため、引数から推測する方法はありません。呼び出し時にテンプレート引数を明示的に指定することで、この関数を呼び出すことができます。

print1<4, 4>(src)

ただし、上で見たように、コンパイラーはNそれ自体を推測できます。M推測できないというだけです。したがって、引数を指定しMてコンパイラに推測させるだけで呼び出しを行うこともできNます。

print1<4>(src)

または、配列への参照を取得するものとして関数テンプレートを宣言することもできます。

template <int M, int N>
void print1(int (&src)[M][N])

これにより、配列からポインタへの変換が抑制されます。なんで?前の例では、パラメータは「の1次元配列へのポインタ」であったことを思い出してくださいint。ただし、この関数テンプレートでは、パラメーターは「の2次元配列への参照int」です。両方のエクステント(次元)は型の一部であるため、コンパイラーは両方を推定できます。


ただし、ほとんどの場合、多次元配列や配列への参照は煩雑であるため、避けるのが最善です。どちらも動的割り当てではうまく機能せず、配列からポインタへの変換が発生しないようにするのは多くの場合多くの問題です。

于 2012-06-01T23:01:42.543 に答える
6

代わりに、次の構文を使用してテンプレート関数を宣言します。

template <int M, int N>
void print1(int (&src)[M][N])
{
}
于 2012-06-01T22:58:48.427 に答える
2

のようなパラメータ宣言int src[4]は、の単なる構文糖衣ですint *src。同じことが配列の配列にも当てはまるので、print2事実上次のようになります。

void print2(int (*src)[4]);

つまり、へのポインタを取りますint[4]

テンプレート関数の場合、これは次のようになります

template <int M, int N>
void print1(int (*src)[N]) { }

さらに、関数を呼び出そうとすると、パラメーターとして指定した配列も。へのポインターに減衰しますint[4]

このことから、コンパイラーはテンプレートパラメーターMを推測できず、一致する関数を見つけることができません。

この問題を回避するには、関数の呼び出しでテンプレートパラメータを明示的に指定するか、参照によって配列を渡すことができます。

template <int M, int N>
void print1(int (&src)[M][N]) { }

参照によって渡す場合、配列の型情報は失われません。パラメータ宣言は、実際には指定された次元の配列への参照を指定し、関数を呼び出すときに、渡された配列はポインタに減衰しません。この情報から、コンパイラーはテンプレートパラメーターを自動的に推測できます。

于 2012-06-01T23:01:54.290 に答える
1

使用するテンプレートを指定する必要があります。例:

print1<4,4>(src);

ただし、テンプレートはコンパイル中にインスタンス化されるため、変数を配置できないことに注意してください(機能しprint1<k,m>(src)ません)。

于 2012-06-01T22:57:23.097 に答える
1

コンパイル時に既知の配列のサイズについての知識を取得し、そこからintテンプレート引数を推測するようにコンパイラーに要求しています。

コンパイラーに型引数を推測させることはできますが、コンパイラーに型(この場合は配列サイズ)から情報を取得して整数テンプレートパラメーターを推測させることはできません。

たとえば、コンパイラが型をint [4] [4]と推定するため、これは機能します。

template <class T>
void printFoo(T t)
{
}

もちろん、このようなテンプレートを使用すると機能します。

print1<4,4>(src);
于 2012-06-01T22:57:41.657 に答える
0

テンプレートパラメータを渡す必要があります。このような:

print1<4,4>(src);
于 2012-06-01T22:57:06.790 に答える
0

テンプレート関数呼び出しは、型に対してのみテンプレート引数の推定を利用できます。非型テンプレート引数を推測することはできません。

于 2012-06-02T05:06:06.480 に答える