16

これはここ数日私を悩ませてきたものです。解決することは不可能だと思いますが、テンプレートの魔法を見たことがあります。

ここに行きます:

標準のC++配列の要素数を取得するには、マクロ(1)またはタイプセーフなインライン関数(2)のいずれかを使用できます。

(1)

#define sizeof_array(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))

(2)

template <typename T>
size_t sizeof_array(const T& ARRAY){
    return (sizeof(ARRAY)/sizeof(ARRAY[0]));
}

ご覧のとおり、最初の1つはマクロであるという問題があり(今のところ問題だと思います)、もう1つはコンパイル時に配列のサイズを取得できないという問題があります。つまり、私は書くことができません:

enum ENUM{N=sizeof_array(ARRAY)};

また

BOOST_STATIC_ASSERT(sizeof_array(ARRAY)==10);// Assuming the size 10..

これが解決できるかどうか誰かが知っていますか?

更新

この質問は、constexprが導入される前に作成されました。今日では、次のように簡単に使用できます。

template <typename T>
constexpr auto sizeof_array(const T& iarray) {
    return (sizeof(iarray) / sizeof(iarray[0]));
}
4

10 に答える 10

22

ここから次のことを試してください:

template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&array)[N] ))[N];
#define mycountof( array ) (sizeof( _ArraySizeHelper( array ) ))

int testarray[10];
enum { testsize = mycountof(testarray) };

void test() {
    printf("The array count is: %d\n", testsize);
}

「アレイ数は10です」と出力されます。

于 2009-09-30T20:37:04.403 に答える
19

C ++ 1xでは、次のconstexprことが可能になります。

template <typename T, size_t N>
constexpr size_t countof(T(&)[N])
{
    return N;
}
于 2009-09-30T20:21:19.620 に答える
8

私が考えることができる最高のものはこれです:

template <class T, std::size_t N>
char (&sizeof_array(T (&a)[N]))[N];

// As litb noted in comments, you need this overload to handle array rvalues
// correctly (e.g. when array is a member of a struct returned from function),
// since they won't bind to non-const reference in the overload above.
template <class T, std::size_t N>
char (&sizeof_array(const T (&a)[N]))[N];

これは別のものと一緒に使用する必要がありますsizeof

int main()
{
    int a[10];
    int n = sizeof(sizeof_array(a));
    std::cout << n << std::endl;
}

[編集]

考えてみると、マクロを除けば、C++03では1回の「関数のような呼び出し」でこれを行うことはおそらく不可能だと思います。その理由は次のとおりです。

一方では、配列のサイズを取得するために(直接、またはsizeofあなたが行うように経由して)テンプレートパラメータの推定が明らかに必要になります。ただし、テンプレートパラメータの推定は関数にのみ適用され、クラスには適用されません。つまり、タイプreference-to-array-of-NのテンプレートパラメータRを持つことができます。ここで、Nは別のテンプレートパラメータですが、呼び出しの時点でRとNの両方を指定する必要があります。RからNを推定したい場合は、関数呼び出しのみがそれを実行できます。

一方、関数呼び出しを含む式を定数にする唯一の方法は、内部にある場合sizeofです。それ以外の場合(関数の戻り値で静的メンバーまたは列挙型メンバーにアクセスする場合など)でも、関数呼び出しを実行する必要があります。これは、これが定数式ではないことを意味します。

于 2009-09-30T20:37:51.250 に答える
6

それはあなたが探しているものとは正確には異なりますが、それは近いです- winnt.h#$%^が何をしているのかについての説明を含むスニペット:

//
// RtlpNumberOf is a function that takes a reference to an array of N Ts.
//
// typedef T array_of_T[N];
// typedef array_of_T &reference_to_array_of_T;
//
// RtlpNumberOf returns a pointer to an array of N chars.
// We could return a reference instead of a pointer but older compilers do not accept that.
//
// typedef char array_of_char[N];
// typedef array_of_char *pointer_to_array_of_char;
//
// sizeof(array_of_char) == N
// sizeof(*pointer_to_array_of_char) == N
//
// pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T);
//
// We never even call RtlpNumberOf, we just take the size of dereferencing its return type.
// We do not even implement RtlpNumberOf, we just decare it.
//
// Attempts to pass pointers instead of arrays to this macro result in compile time errors.
// That is the point.
//
extern "C++" // templates cannot be declared to have 'C' linkage
template <typename T, size_t N>
char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N];

#define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A)))

マクロはRTL_NUMBER_OF_V2()、より読みやすいARRAYSIZE()マクロで使用されることになります。

MatthewWilsonの「ImperfectC++」の本にも、ここで使用されている手法についての説明があります。

于 2009-09-30T22:07:40.397 に答える
6

問題

私はアディサクの答えが好きです:

template <typename T, size_t N>
char ( &_ArraySizeHelper( T (&arr)[N] ))[N];
#define COUNTOF( arr ) (sizeof( _ArraySizeHelper( arr ) ))

これは、MicrosoftがVS2008の_countofマクロに使用するものであり、いくつかの優れた機能があります。

  • コンパイル時に動作します
  • これはタイプセーフです(つまり、ポインターを指定するとコンパイル時エラーが発生し、配列が非常に簡単に劣化します)

しかし、 Georgが指摘しているように、このアプローチはテンプレートを使用するため、C++03のローカルタイプでの動作が保証されていません

void i_am_a_banana() {
  struct { int i; } arr[10];
  std::cout << COUNTOF(arr) << std::endl; // forbidden in C++03
}

幸いなことに、私たちは運が悪いわけではありません。

ソリューション

Ivan Johnsonは、すべてのアカウントで成功する巧妙なアプローチを考案しました。これは、タイプセーフで、コンパイル時であり、ローカルタイプで機能します。

#define COUNTOF(arr) ( \
   0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + \
   0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + \
   sizeof(arr) / sizeof((arr)[0]) )

struct Bad_arg_to_COUNTOF {
   class Is_pointer; // incomplete
   class Is_array {};
   template <typename T>
   static Is_pointer check_type(const T*, const T* const*);
   static Is_array check_type(const void*, const void*);
};

興味のある方は、標準のsizeofベースの配列サイズマクロの前に2つの「テスト」を挿入することで機能します。これらのテストは最終的な計算には影響しませんが、非配列型のコンパイルエラーを生成するように設計されています。

  1. arr整数、列挙型、ポインター、または配列で ない限り、最初のテストは失敗します。reinterpret_cast<const T*>他のタイプでは失敗するはずです。
  2. 2番目のテストは、整数型、列挙型、またはポインター型では失敗します。

    整数型と列挙型は、ポインタを期待check_typeしているため、一致するバージョンがないため失敗します。check_type

    テンプレート化されたバージョンと一致するため、ポインタ型は失敗しますが、テンプレート化されcheck_typeた戻り型(Is_pointercheck_typeは不完全であり、エラーが発生します。

    型の配列のアドレスを取得すると、ポインタからポインタではなく、配列へのポインタT が得られるため、配列型は渡されます。T (*)[]これは、のテンプレートバージョンがcheck_type一致しないことを意味します。SFINAEのおかげで、コンパイラーはテンプレート化されていないバージョンのに移行しcheck_typeます。これは、ポインターの任意のペアを受け入れる必要があります。テンプレート化されていないバージョンの戻りタイプは完全に定義されているため、エラーは発生しません。また、現在テンプレートを扱っていないため、ローカルタイプは正常に機能します。

于 2011-06-06T17:56:07.537 に答える
4

Microsoftのみのプラットフォームを使用している場合は、_countofマクロを利用できます。これは非標準の拡張機能であり、配列内の要素の数を返します。ほとんどのcountofスタイルのマクロに対する利点は、配列以外の型で使用するとコンパイルエラーが発生することです。

以下は問題なく動作します(VS 2008 RTM)

static int ARRAY[5];
enum ENUM{N=_countof(ARRAY)};

しかし、繰り返しになりますが、これはMS固有であるため、これは機能しない可能性があります。

于 2009-09-30T20:10:40.960 に答える
3

一般的にそれを解決することはできません。それが、ブースト配列のような配列ラッパーの理由の1つです(もちろん、stlスタイルの動作もあります)。

于 2009-09-30T20:12:49.490 に答える
2

現在のC++標準のマクロがないと、配列のsizeofをコンパイル時定数として取得することはできないようです(配列サイズを推定する関数が必要ですが、コンパイル時定数が必要な場合は関数呼び出しは許可されません) 。[編集:しかし、ミナエフの素晴らしい解決策を見てください!]

ただし、テンプレートのバージョンもタイプセーフではなく、マクロと同じ問題があります。ポインターも受け入れ、特に配列がポインターに減衰します。ポインタを受け入れる場合、sizeof(T *)/ sizeof(T)の結果は意味がありません。

より良い:

template <typename T, size_t N>
size_t sizeof_array(T (&)[N]){
    return N;
}
于 2009-09-30T20:38:18.100 に答える
2

C ++ 0xがない場合、私が得ることができる最も近いものは次のとおりです。

#include <iostream>

template <typename T>
struct count_of_type
{
};


template <typename T, unsigned N>
struct count_of_type<T[N]> 
{
    enum { value = N };
};

template <typename T, unsigned N>
unsigned count_of ( const T (&) [N] )
{
    return N;
};


int main ()
{
    std::cout << count_of_type<int[20]>::value << std::endl;
    std::cout << count_of_type<char[42]>::value << std::endl;

    // std::cout << count_of_type<char*>::value << std::endl; // compile error

    int foo[1234];

    std::cout << count_of(foo) << std::endl;

    const char* bar = "wibble";

    // std::cout << count_of( bar ) << std::endl; // compile error

    enum E1 { N = count_of_type<int[1234]>::value } ;

    return 0;
}

これは、変数を渡すことができる関数、または型を渡すことができるテンプレートのいずれかを提供します。この関数をコンパイル時定数に使用することはできませんが、テンプレートパラメータとしてのみ使用している場合でも、ほとんどの場合、型はわかっています。

于 2009-09-30T20:53:19.987 に答える
2

これで、STLライブラリを使用して、アレイサイズのコンパイル時間を決定/選択できます。

#include <iostream>
#include <array>

template<class T>
void test(T t)
{
    int a[std::tuple_size<T>::value]; // can be used at compile time
    std::cout << std::tuple_size<T>::value << '\n';
}

int main()
{
    std::array<float, 3> arr;
    test(arr);
}

出力:3

于 2017-08-01T00:49:48.367 に答える