这个宏可以转换为函数吗?

Rob*_*Rob 8 c++ macros c-preprocessor

在重构代码并摆脱我们现在被教导讨厌的所有#defines时,我偶然发现了这个用于计算结构中元素数量的美:

#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))
Run Code Online (Sandbox Code Playgroud)

非常有用,但是它可以转换为内联函数或模板吗?

好的,ARRAYSIZE会是一个更好的名字,但这是遗留代码(不知道它来自哪里,至少15年)所以我按原样粘贴它.

KTC*_*KTC 19

如上所述,代码实际上计算出数组中元素的数量,而不是结构.我会在需要时明确写出sizeof()除法.如果我要使它成为一个函数,我想在其定义中明确表示它期望一个数组.

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

上面的内容类似于xtofl,除了它防止传递一个指针(指向一个动态分配的数组)并错误地得到错误的答案.

编辑:根据JohnMcG简化. 编辑:内联.

不幸的是,上面没有提供编译时答案(即使编译器内联并优化它是一个常量),因此不能用作编译时常量表达式.即它不能用作声明静态数组的大小.在C++ 0x下,如果用constexpr替换关键字inline(constexpr隐式内联),这个问题就会消失.

constexpr size_t array_size(const T (&array)[SIZE])
Run Code Online (Sandbox Code Playgroud)

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*> {};
Run Code Online (Sandbox Code Playgroud)

并通过写作使用

ArraySize<BOOST_TYPEOF(foo)>::size
Run Code Online (Sandbox Code Playgroud)

其中foo是数组的名称.


jwf*_*arn 5

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 ) 
Run Code Online (Sandbox Code Playgroud)

使用测试代码(使用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;
}
Run Code Online (Sandbox Code Playgroud)

此解决方案在编译时拒绝非数组类型,因此它不会像宏版本那样被指针混淆.


小智 5

到目前为止,没有人提出了一种可移植的方法来获取数组的大小而只有一个数组的实例而不是它的类型.(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
}
Run Code Online (Sandbox Code Playgroud)
  • 当只有值时,它才有效.
  • 它是可移植的,只使用std-C++.
  • 它失败并出现描述性错误消息.
  • 它没有评估价值.(我无法想到这会出现问题的情况,因为函数不能返回数组类型,但最好是安全而不是遗憾.)
  • 它返回大小为编译时常量.

我将构造包装到宏中以获得一些不错的语法.如果你想摆脱它,你唯一的选择是手动进行替换.