web-dev-qa-db-ja.com

誰かが私に配列のサイズを与えるこのテンプレートコードを説明できますか?

template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
    return n;
}

取得できないのは、このテンプレート関数のパラメーターです。配列の要素数としてnを渡すと、配列はどうなりますか?

62
marsol0x

さて、まず、配列から値を取得しようとすると、最初の要素へのポインタが得られることを理解する必要があります。

int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost

参照は、オブジェクトの正確なタイプまたは基本クラスタイプを使用して参照します。重要なのは、テンプレートが参照によって配列を取ることです。 C++にはパラメーターとして存在しない配列(それらへの参照ではありません)。パラメータに配列型を指定すると、代わりにポインタになります。そのため、渡された配列のサイズを知りたい場合は、参照を使用する必要があります。通常、関数テンプレートの場合と同様に、サイズと要素タイプは自動的に推定されます。次のテンプレート

template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
    return n;
}

以前に定義した配列aで呼び出すと、次の関数が暗黙的にインスタンス化されます。

size_t array_size(const int (&)[3]) {
    return 3;
}

これは次のように使用できます:

size_t size_of_a = array_size(a);

少し前に作り上げたバリエーションがあります[編集:誰かがすでに同じ考えを持っていたことがわかります here ]コンパイル時の値。値を直接返す代わりに、テンプレートはnに応じた戻り値の型を提供します。

template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];

配列にn要素がある場合、戻り値の型はサイズnおよび要素型charの配列への参照です。これで、渡された配列のコンパイル時に決定されたサイズを取得できます。

size_t size_of_a = sizeof(array_size(a));

char要素を持つnの配列にはsizeof nがあるため、指定された配列の要素数もわかります。コンパイル時に、そうすることができます

int havingSameSize[sizeof(array_size(a))];

関数が実際に呼び出されることはないため、定義する必要がないため、本体はありません。少し問題が解決できれば幸いです。

このように考えてください。たくさんの関数があるとしましょう。

// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
    return 1;
}

size_t array_size(const int (&)[2])
{
    return 2;
}

size_t array_size(const int (&)[3])
{
    return 3;
}
// etc...

これを呼び出すと、どの関数が呼び出されますか?

int a[2];
array_size(a);  

配列サイズをテンプレート化すると、次のようになります。

template <int n>
size_t array_size(const int (&)[n])
{
    return n;
}

コンパイラーは、呼び出すパラメーターと一致するバージョンのarray_sizeをインスタンス化しようとします。したがって、10 intの配列で呼び出すと、array_sizeはn = 10でインスタンス化されます。

次に、型をテンプレート化するだけで、int配列以外のものでも呼び出すことができます。

template <typename T, int n>
size_t array_size(const T (&)[n])
{
    return n;
}

これで完了です。

編集(&)に関するメモ

&を囲む括弧は、int参照の配列(不正)とint配列の参照(必要なもの)を区別するために必要です。宣言がある場合、[]の優先順位は&よりも高いため、次のようになります。

const int &a[1];

演算子の優先順位のため、intへのconst参照の1要素の配列になります。最初に&を適用したい場合は、括弧で強制する必要があります:

const int (&a)[1];  

これで、intの1要素配列へのconst参照ができました。関数パラメーターリストでは、使用しない場合はパラメーターの名前を指定する必要がないため、名前を削除できますが、括弧はそのままにしておきます。

size_t array_size(const int (&)[1])
21
Eclipse

配列には何も起こりません。これは、テンプレート関数のシグネチャを解決するために使用される未使用のパラメーターです。

また、テンプレート引数として使用することもできませんが、それは別個のnitです。

1
MSN

「constexpr」を持たない私たちのために、コンパイル時のconstとして結果を取得する少し奇妙な方法:

#include <iostream>

namespace
{

    template <size_t V>
    struct helper
    {
        enum
        {
            value = V
        };
    };


    template<typename T, size_t Size>
    auto get_size(T(&)[Size]) -> helper < Size >
    {
        return helper < Size >() ;
    }

    template<typename T>
    struct get_value
    {
        enum
        {
            value = T::value
        };
    };

}

int main()
{
    std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}
0
TechPriest