Безопасный sizeof для массивов в С++

Иногда приходится иметь дело с обычными массивами и указателями на них в С++. Также иногда встает задача определения количества элементов массиве на стадии компиляции.

Например, это можно слелать так:

#define arraysize(array) (sizeof(array) / sizeof(array[0]))

Но тут есть одна проблема. Если случайно передать в этот макрос не массив, а просто указатель, что ошибки компиляции не будет, но значение будет далеко от задуманного.

Вчера прочитал на Харбе (кстати, отличная статья), что в С++ можно сделать этот макрос более безопасным.

Вот код, который используется в Chrome:

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

Выглядит немного запутанно, но можно разобраться:

  • T (&array)[N] - определение массива (T array[N]), который передается по ссылке
  • char (&ArraySizeHelper(...)[N] - функция, возвращающая массив по ссылке
  • sizeof(ArraySizeHelper(array)) - определение размера возвращаемого функцией значения
  • Все это шаблон функции, параметризированный типом массива и его размером, который автоматически определяется компилятором при раскрытии шаблона. Так как функция реально не вызывается, то и тело ее определять не нужно.

Если честно, додуматься до такого непросто. Но макрос весьма хорош. Я взял себе на вооружение.

Кстати, можно поиграться с sizeof() от типа возвращаемого функцией значения:

#include <iostream>
#include <string>

std::string f() {
  return std::string();
}

int main() {
  std::cout << sizeof( (&f)() ) << std::endl;
  std::cout << sizeof( std::string ) << std::endl;
  return 0;
}

У меня на VS2010 выводит два раза число “28”.

Интересно, что в чистом С такой номер тоже проходит:

#include <stdio.h>

struct t {
  char x[1024];
};

struct t f() {
  struct t a;
  return a;
}

int main() {
  printf("%d\n", sizeof(struct t));
  printf("%d\n", sizeof( (*f)() ));
  return 0;
}

Печатает два раза “1024”.


Оригинальный пост | Disclaimer

Комментарии