可以将此宏转换为函数吗?
在重构代码并使自己摆脱我们现在被教导讨厌的所有#define时,我遇到了用于计算结构中元素数量的这种美感:
#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))
它非常有用,但是可以转换为内联函数或者模板吗?
好的,ARRAYSIZE会是一个更好的名称,但这是旧代码(不知道它来自何处,至少有15年的历史了),所以我按原样粘贴了它。
解决方案
- 功能,无模板功能,是
- 模板,我想是这样(但C ++
- 模板不是我的事)
编辑:从道格的代码
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) }; }
有人告诉我第二个不起作用。 OTOH之类的东西应该是可行的,我只是没有使用足够的C ++来修复它。
有关编译时内容的C ++(和D)模板页面
是的,可以将其作为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帖子,这是有关使用轻型模板元编程在编译时使用类进行此操作的一种不错的方法。
我不认为这确实可以计算出结构中元素的数量。如果打包了结构,并且使用了小于指针大小的内容(例如32位系统上的char),那么结果将是错误的。另外,如果该结构包含一个结构,那么我们也是错误的!
宏名称错误,应称为ARRAYSIZE。它用于确定在编译时大小固定的数组中的元素数。这是一种工作方式:
char foo[ 128 ]; // In reality, you'd have some constant or constant expression as the array size. for( unsigned i = 0; i < STRUCTSIZE( foo ); ++i ) { }
使用起来有点脆弱,因为我们可能会犯以下错误:
char* foo = new char[128]; for( unsigned i = 0; i < STRUCTSIZE( foo ); ++i ) { }
现在,我们将迭代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 :)
如果宏的名称作为宏参数传入,则该宏的表达式会返回数组中元素的数量,因此该宏的名称会引起误解。
对于其他类型,如果类型是指针,则将或者多或者少地失去意义,否则将出现语法错误。
通常,该宏的名称类似于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 ++"对此处的情况进行了很好的处理(第21章第12.3节,第211-213页,数组和指针的维数())。
如前所述,代码实际上计算出数组中的元素数量,而不是结构。我只需要在需要时显式写出sizeof()分区。如果要使它成为一个函数,我想在其定义中明确指出它正在期待一个数组。
template<typename T,int SIZE> inline size_t array_size(const T (&array)[SIZE]) { return SIZE; }
上面的代码类似于xtofl的代码,除了它防止传递指向它的指针(表示指向动态分配的数组)并防止错误地得到错误答案。
编辑:根据JohnMcG简化。
编辑:内联。
不幸的是,以上方法没有提供编译时间答案(即使编译器确实内联并优化了它以使其成为一个常量),因此不能用作编译时间常量表达式。即它不能用作声明静态数组的大小。在C ++ 0x下,如果用constexpr替换关键字inline(constexpr是隐式内联的),此问题就消失了。
constexpr size_t array_size(const T (&array)[SIZE])
jwfearn的解决方案在编译时有效,但涉及到具有一个typedef,它可以有效地"节省"新名称声明中的数组大小。然后通过使用该新名称初始化常量来计算数组大小。在这种情况下,也可以从一开始就简单地将数组大小保存为一个常数。
Martin York发布的解决方案也可以在编译时使用,但是涉及使用非标准的typeof()运算符。解决该问题的方法是等待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
其中foo是数组的名称。
xtofl对于找到数组大小有正确的答案。查找size的大小不需要宏或者模板,因为sizeof()应该做得很好。
我同意预处理器是有害的,但是在某些情况下,它是最不有害的。
简化@KTC,因为我们在template参数中具有数组的大小:
template<typename T, int SIZE> int arraySize(const T(&arr)[SIZE]) { return SIZE; }
缺点是,对于每个Typename,Size组合,我们都将在二进制文件中拥有此副本。
作为JohnMcG的答案,但是
缺点是,对于每个Typename,Size组合,我们都将在二进制文件中拥有此副本。
这就是为什么要使其成为内联模板函数。
在这里详细回答:
数组大小确定第1部分
和这里:
数组大小确定第2部分。
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; }
此解决方案在编译时拒绝非数组类型,因此不会像宏版本那样被指针混淆。
我更喜欢[BCS]建议的枚举方法(可以将此宏转换为函数吗?)
这是因为我们可以在编译器期望编译时间常数的地方使用它。该语言的当前版本不允许我们将函数结果用于编译时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],©[0]); std::copy(©[0],©[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]; }
Windows特定:
CRT为此提供了宏_countof()。
指向MSDN上的文档的链接
到目前为止,没有一个人提出了一种可移植的方法来获取仅当有数组实例而不是数组类型的数组时的大小。 (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 ++。
- 它失败,并显示描述性错误消息。
- 它不评估值。 (我想不出会出现问题的情况,因为函数无法返回数组类型,但总比后悔要安全。)
- 它返回大小作为编译时常量。
我将结构包装到一个宏中以具有一些不错的语法。如果要摆脱它,我们唯一的选择是手动进行替换。
对于C99样式的可变长度数组,似乎唯一的可行方法是纯宏方法(sizeof(arr)/ sizeof(arr [0]))。