문제

코드를 리팩터링하고 우리가 싫어하도록 배운 모든 #define을 제거하는 동안 구조의 요소 수를 계산하는 데 사용되는 다음과 같은 아름다움을 발견했습니다.

#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))

그 자체로 매우 유용하지만 인라인 함수나 템플릿으로 변환할 수 있나요?

좋아, ARRAYSIZE가 더 나은 이름이겠지만 이는 레거시 코드(어디에서 왔는지 모르며 최소 15년 이상 된 코드임)이므로 '있는 그대로' 붙여넣었습니다.

도움이 되었습니까?

해결책

언급한 대로 코드는 실제로 구조체가 아닌 배열의 요소 수를 계산합니다.나는 필요할 때 sizeof() 분할을 명시적으로 작성합니다.함수로 만들려면 정의에서 배열이 필요하다는 점을 분명히 하고 싶습니다.

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

위의 내용은 비슷합니다 xtofl의, 포인터를 전달하고(동적으로 할당된 배열을 가리킨다는 뜻) 실수로 잘못된 대답을 얻는 것을 방지한다는 점만 제외됩니다.

편집하다:다음과 같이 단순화되었습니다. 존맥지. 편집하다:인라인.

불행하게도 위의 내용은 컴파일 시간 응답을 제공하지 않으므로(컴파일러가 내부적으로 이를 상수로 인라인 및 최적화하더라도) 컴파일 시간 상수 표현식으로 사용할 수 없습니다.즉.정적 배열을 선언하기 위해 크기로 사용할 수 없습니다.C++0x에서는 키워드를 바꾸면 이 문제가 사라집니다. 인라인 ~에 의해 constexpr (consexpr은 암시적으로 인라인입니다).

constexpr size_t array_size(const T (&array)[SIZE])

jwfearn의 솔루션은 컴파일 시간 동안 작동하지만 새 이름 선언에서 배열 크기를 효과적으로 "절약"하는 typedef를 포함합니다.그런 다음 새 이름을 통해 상수를 초기화하여 배열 크기를 계산합니다.이러한 경우에는 처음부터 배열 크기를 상수로 저장하는 것이 좋습니다.

마틴 요크의 게시된 솔루션은 컴파일 타임에도 작동하지만 비표준 사용을 포함합니다. 유형() 운영자.이에 대한 해결 방법은 C++0x를 기다렸다가 사용하는 것입니다. Decltype (언제까지는 이 문제에 대해 실제로 필요하지 않을 것입니다. constexpr).또 다른 대안은 Boost.Typeof를 사용하는 것입니다. 이 경우 우리는 다음과 같이 끝납니다.

#include <boost/typeof/typeof.hpp>

template<typename T>
struct ArraySize
{
    private:    static T x;
    public:     enum { size = sizeof(T)/sizeof(*x)};
};
template<typename T>
struct ArraySize<T*> {};

그리고 쓰기에 사용됩니다

ArraySize<BOOST_TYPEOF(foo)>::size

어디 배열의 이름입니다.

다른 팁

지금까지 배열의 인스턴스만 있고 해당 유형이 없는 경우 배열의 크기를 가져오는 이식 가능한 방법을 제안한 사람은 없습니다.(typeof와 _countof는 이식성이 없으므로 사용할 수 없습니다.)

나는 다음과 같은 방법으로 할 것입니다 :

template<int n>
struct char_array_wrapper{
    char result[n];
};

template<typename T, int s>
char_array_wrapper<s> the_type_of_the_variable_is_not_an_array(const T (&array)[s]){
}


#define ARRAYSIZE_OF_VAR(v) sizeof(the_type_of_the_variable_is_not_an_array(v).result)

#include <iostream>
using namespace std;

int main(){
    int foo[42];
    int*bar;
    cout<<ARRAYSIZE_OF_VAR(foo)<<endl;
    // cout<<ARRAYSIZE_OF_VAR(bar)<<endl;  fails
}
  • 값이 주변에 있을 때만 작동합니다.
  • 이식성이 뛰어나며 std-C++만 사용합니다.
  • 설명적인 오류 메시지와 함께 실패합니다.
  • 가치를 평가하지 않습니다.(배열형이 함수에 의해 반환될 수 없기 때문에 이것이 문제가 될 상황은 생각나지 않지만, 미안하기보다는 안전한 것이 낫습니다.)
  • 크기를 컴파일타임 상수로 반환합니다.

적절한 구문을 갖기 위해 구성을 매크로로 래핑했습니다.이를 제거하려면 유일한 옵션은 수동으로 대체하는 것입니다.

KTC의 솔루션은 깨끗하지만 컴파일 타임에 사용할 수 없으며 코드 팽창 및 함수 호출 오버헤드를 방지하기 위해 컴파일러 최적화에 의존합니다.

런타임 비용이 전혀 들지 않는 컴파일 시간 전용 메타함수를 사용하여 배열 크기를 계산할 수 있습니다. BCS 올바른 방향으로 가고 있었지만 그 해결책은 올바르지 않습니다.

내 해결책은 다음과 같습니다.

// asize.hpp
template < typename T >
struct asize; // no implementation for all types...

template < typename T, size_t N >
struct asize< T[N] > { // ...except arrays
    static const size_t val = N;
};

template< size_t N  >
struct count_type { char val[N]; };

template< typename T, size_t N >
count_type< N > count( const T (&)[N] ) {}

#define ASIZE( a ) ( sizeof( count( a ).val ) ) 
#define ASIZET( A ) ( asize< A >::val ) 

테스트 코드 사용(사용 Boost.StaticAssert 컴파일 타임 전용 사용법을 보여주기 위해):

// asize_test.cpp
#include <boost/static_assert.hpp>
#include "asize.hpp"

#define OLD_ASIZE( a ) ( sizeof( a ) / sizeof( *a ) )

typedef char C;
typedef struct { int i; double d; } S;
typedef C A[42];
typedef S B[42];
typedef C * PA;
typedef S * PB;

int main() {
    A a; B b; PA pa; PB pb;
    BOOST_STATIC_ASSERT( ASIZET( A ) == 42 );
    BOOST_STATIC_ASSERT( ASIZET( B ) == 42 );
    BOOST_STATIC_ASSERT( ASIZET( A ) == OLD_ASIZE( a ) );
    BOOST_STATIC_ASSERT( ASIZET( B ) == OLD_ASIZE( b ) );
    BOOST_STATIC_ASSERT( ASIZE( a ) == OLD_ASIZE( a ) );
    BOOST_STATIC_ASSERT( ASIZE( b ) == OLD_ASIZE( b ) );
    BOOST_STATIC_ASSERT( OLD_ASIZE( pa ) != 42 ); // logic error: pointer accepted
    BOOST_STATIC_ASSERT( OLD_ASIZE( pb ) != 42 ); // logic error: pointer accepted
 // BOOST_STATIC_ASSERT( ASIZE( pa ) != 42 ); // compile error: pointer rejected
 // BOOST_STATIC_ASSERT( ASIZE( pb ) != 42 ); // compile error: pointer rejected
    return 0;
}

이 솔루션은 컴파일 타임에 배열이 아닌 유형을 거부하므로 매크로 버전처럼 포인터로 인해 혼동되지 않습니다.

매크로에는 오해의 소지가 있는 이름이 있습니다. 매크로의 표현식은 배열 이름이 매크로 매개변수로 전달되는 경우 배열의 요소 수를 반환합니다.

다른 유형의 경우 유형이 포인터이면 다소 의미 없는 결과를 얻거나 구문 오류가 발생합니다.

일반적으로 해당 매크로의 이름은 NUM_ELEMENTS() 또는 실제 유용성을 나타내는 이름으로 지정됩니다.C에서는 매크로를 함수로 바꾸는 것이 불가능하지만 C++에서는 템플릿을 사용할 수 있습니다.

내가 사용하는 버전은 Microsoft의 winnt.h 헤더에 있는 코드를 기반으로 합니다. (이 코드 조각을 게시하는 것이 공정한 사용 범위를 벗어나는 경우 알려주시기 바랍니다.)

//
// Return the number of elements in a statically sized array.
//   DWORD Buffer[100];
//   RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))

#if defined(__cplusplus) && \
    !defined(MIDL_PASS) && \
    !defined(RC_INVOKED) && \
    !defined(_PREFAST_) && \
    (_MSC_FULL_VER >= 13009466) && \
    !defined(SORTPP_PASS)
//
// 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)))

//
// This does not work with:
//
// void Foo()
// {
//    struct { int x; } y[2];
//    RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation
// }
//
// You must instead do:
//
// struct Foo1 { int x; };
//
// void Foo()
// {
//    Foo1 y[2];
//    RTL_NUMBER_OF_V2(y); // ok
// }
//
// OR
//
// void Foo()
// {
//    struct { int x; } y[2];
//    RTL_NUMBER_OF_V1(y); // ok
// }
//
// OR
//
// void Foo()
// {
//    struct { int x; } y[2];
//    _ARRAYSIZE(y); // ok
// }
//

#else
#define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A)
#endif

#ifdef ENABLE_RTL_NUMBER_OF_V2
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V2(A)
#else
#define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A)
#endif

//
// ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2, and uses
// it regardless of ENABLE_RTL_NUMBER_OF_V2
//
// _ARRAYSIZE is a version useful for anonymous types
//
#define ARRAYSIZE(A)    RTL_NUMBER_OF_V2(A)
#define _ARRAYSIZE(A)   RTL_NUMBER_OF_V1(A)

또한 Matthew Wilson의 책 "Imperfect C++"에는 여기서 무슨 일이 일어나고 있는지 잘 설명되어 있습니다(섹션 14.3 - 페이지 211-213 - 배열 및 포인터 - 차원()).

매크로 이름이 잘못되었습니다. ARRAYSIZE라고 해야 합니다.컴파일 타임에 크기가 고정된 배열의 요소 수를 결정하는 데 사용됩니다.작동 방법은 다음과 같습니다.

char foo[ 128 ];// 실제로, 당신은 배열 크기로 일정하거나 일정한 표현식을 가질 것입니다.

for(부호 없는 i = 0;i <structsize (foo);++나는 ) { }

다음과 같은 실수를 할 수 있기 때문에 사용하기가 다소 불안정합니다.

char* foo = 새로운 char[128];

for(부호 없는 i = 0;i <structsize (foo);++나는 ) { }

이제 i = 0에서 < 1까지 반복하고 머리카락을 찢어냅니다.

템플릿 함수의 유형은 템플릿 클래스의 유형과 달리 자동으로 추론됩니다.더 간단하게 사용할 수 있습니다:

template< typename T > size_t structsize( const T& t ) { 
  return sizeof( t ) / sizeof( *t ); 
}


int ints[] = { 1,2,3 };
assert( structsize( ints ) == 3 );

하지만 구조체에서는 작동하지 않는다는 데 동의합니다.배열에서 작동합니다.그래서 나는 그것을 Arraysize라고 부르고 싶습니다 :)

템플릿 인수에 배열 크기가 있으므로 @KTC를 단순화합니다.

template<typename T, int SIZE>
int arraySize(const T(&arr)[SIZE])
{
    return SIZE;
}

단점은 모든 유형 이름, 크기 조합에 대해 바이너리에 이 복사본이 있다는 것입니다.

  • 기능, 템플릿 기능 없음, 예
  • 템플릿인 것 같아요. (하지만 C++
  • 템플릿은 내 것이 아닙니다)

편집하다: Doug의 코드에서

template <typename T>
uint32_t StructSize()  // This might get inlined to a constant at compile time
{
   return sizeof(T)/sizeof(*T);
}

// or to get it at compile time for shure

class StructSize<typename T>
{
   enum { result = sizeof(T)/sizeof(*T) };
}

2번째는 안 된다고 하더군요.OTOH는 실행 가능해야 하지만 C++를 사용하여 문제를 해결할 만큼 충분하지 않습니다.

컴파일 시간 관련 C++(및 D) 템플릿에 대한 페이지

나는 [BCS](in에서 제안한 열거형 방법을 선호합니다. 이 매크로를 함수로 변환할 수 있나요?)

이는 컴파일러가 컴파일 시간 상수를 기대하는 곳에서 사용할 수 있기 때문입니다.현재 버전의 언어에서는 컴파일 시간 const에 대한 함수 결과를 사용할 수 없지만 다음 버전의 컴파일러에서는 이것이 가능하다고 생각합니다.

이 방법의 문제점은 '*' 연산자를 오버로드한 클래스와 함께 사용할 때 컴파일 시간 오류를 생성하지 않는다는 것입니다(자세한 내용은 아래 코드 참조).

불행하게도 'BCS'에서 제공하는 버전은 예상대로 컴파일되지 않으므로 내 버전은 다음과 같습니다.

#include <iterator>
#include <algorithm>
#include <iostream>


template<typename T>
struct StructSize
{
    private:    static T x;
    public:      enum { size = sizeof(T)/sizeof(*x)};
};

template<typename T>
struct StructSize<T*>
{
    /* Can only guarantee 1 item (maybe we should even disallow this situation) */
    //public:     enum { size = 1};
};

struct X
{
    int operator *();
};


int main(int argc,char* argv[])
{
    int data[]                                  = {1,2,3,4,5,6,7,8};
    int copy[ StructSize<typeof(data)>::size];

    std::copy(&data[0],&data[StructSize<typeof(data)>::size],&copy[0]);
    std::copy(&copy[0],&copy[StructSize<typeof(copy)>::size],std::ostream_iterator<int>(std::cout,","));

    /*
     * For extra points we should make the following cause the compiler to generate an error message */
    X   bad1;
    X   bad2[StructSize<typeof(bad1)>::size];
}

나는 그것이 실제로 구조의 요소 수를 계산한다고 생각하지 않습니다.구조가 패킹되어 있고 포인터 크기보다 작은 것(예: 32비트 시스템의 char)을 사용한 경우 결과가 잘못된 것입니다.또한 구조체에 구조체가 포함되어 있으면 틀린 것입니다!

예, C++로 템플릿을 만들 수 있습니다.

template <typename T>
size_t getTypeSize()
{
   return sizeof(T)/sizeof(*T);
}

사용:

struct JibbaJabba
{
   int int1;
   float f;
};

int main()
{
    cout << "sizeof JibbaJabba is " << getTypeSize<JibbaJabba>() << std::endl;
    return 0;
}

일부 라이트 템플릿 메타프로그래밍을 사용하여 컴파일 타임에 클래스에서 이를 수행하는 멋진 방법에 대해서는 위 또는 아래 BCS의 게시물을 참조하세요.

xtofl에는 배열 크기를 찾는 정답이 있습니다.sizeof()가 잘 작동해야 하기 때문에 구조체의 크기를 찾는 데 매크로나 템플릿이 필요하지 않습니다.

나는 동의한다 전처리기는 사악하다, 하지만 그럴 경우도 있습니다. 대안 중 가장 덜 악한 것.

JohnMcG의 답변이지만

단점은 모든 유형 이름, 크기 조합에 대해 바이너리에 이 복사본이 있다는 것입니다.

그래서 당신은 그것을 만들 것입니다 인라인 템플릿 기능.

여기에 자세히 답변했습니다.배열 크기 결정 1부그리고 여기:배열 크기 결정 2부.

Windows 특정:

매크로가 있어요 _countof() 이 목적을 위해 정확히 CRT에서 제공됩니다.

MSDN 문서에 대한 링크

C99 스타일 가변 길이 배열의 경우 순수 매크로 접근 방식(sizeof(arr) / sizeof(arr[0]))이 작동하는 유일한 방법인 것으로 보입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top