Является ли эталонный размер функции портативным? - PullRequest
2 голосов
/ 31 мая 2010

Ища способ сделать переносимое, безопасное, подсчет элементов для массивов в стиле c, я нашел это решение:

template <typename T, unsigned N>  char (&arrayCountofHelper(T(&)[N]))[N];
#define ARRAY_COUNTOF(arr) (sizeof(arrayCountofHelper(arr)))

Кажется, что arrayCountofHelper на самом деле является ссылкой на функцию, а макрос ARRAY_COUNTOF использует тот факт, что размер функции равен размеру возвращаемого типа.
Работает просто отлично.
Однако, когда я попытался проверить, является ли он портативным, я не нашел никаких доказательств этого. Я не смог найти ссылку на sizeof (ссылку на функцию) в стандарте (14882/1998), и, на самом деле, я не совсем уверен, что мне разрешено даже создавать ссылку на функцию (хотя стандарт упоминает ее несколько раз).
Итак, кто-то знает, где в стандарте я должен искать? (Или, если я как-то неверно истолковал декларацию, как это правильно?)

Спасибо
Орен

P.S. (для тех из вас, кто считает, что я не нашел подходящего решения для моей проблемы)
Я знаю, что всегда могу использовать

#define ARRAY_COUNTOF sizeof(arr)/sizeof(arr[0])

или даже

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

но первый не будет проверять, является ли arr массивом (или указателем), а второй не используется внутри static_assert.
(и я бы использовал std :: tr1 :: array или std :: vector, но это устаревший код, который я поддерживаю)

Ответы [ 2 ]

4 голосов
/ 01 июня 2010

Мы разберем это свободно, я использую INCITS + ISO + IEC + 14882-2003. Я процитирую мелкие вещи, но некоторые более сложные вещи слишком велики, чтобы их цитировать.

sizeof определено в §5.3.3 и гласит (сокращенно):

Оператор sizeof возвращает количество байтов в объектном представлении своего операнда. Операндом является либо выражение, которое не вычисляется, либо идентификатор типа в скобках.

Другими словами, он возвращает размер типа в байтах или находит тип выражения и определяет его размер. У нас нет типа, у нас есть выражение arrayCountofHelper(arr).

Вы можете разобрать это выражение, взглянув на определения primary-expression и postfix-expression, как они определены в §5.1 и §5.2 соответственно. Вы найдете, что это postfix-expression и соответствует требованиям вызова функции (§5.2.2).

Теперь вернемся к sizeof. Мы заботимся только о типе этого выражения вызова функции (чтобы мы могли определить его размер), а §5.2.2 / 3 говорит:

Тип выражения вызова функции - это тип возврата статически выбранной функции [...]. Этот тип должен быть полным типом объекта, ссылочным типом или типом void.

Таким образом, нам нужно найти тип возвращаемого значения функции, которая будет вызываться (помните, это все не оценено) с помощью arrayCountofHelper(arr). arrayCountofHelper - это шаблон функции, который мы будем создавать (§14.7), поэтому нам нужно сделать это до того, как мы создадим экземпляр функции для получения возвращаемого типа.

Все параметры шаблона должны иметь значения (§14.8.2), и, используя правила, определенные в §14.8.2.1, мы найдем T и N путем сопоставления массива, переданного функции, с функциями параметр (который является ссылкой на массив). (Например, если arr было int[10], T было бы int и N было бы 10.) Как только мы получим их, можно будет создать экземпляр функции.

После создания тип возвращаемого значения функции будет char(&)[N]*, ссылка на массив N char. (Если вам нужна помощь в разборе, см. §8.3.5. Есть также вопросы к SO о том, как анализировать «сложные» типы.) Итак, теперь мы нашли тип выражения, мы должны принять его размер.

§5.3.3 / 2 определяет, как sizeof работает со ссылками и массивами (выделено мое):

При применении к ссылке или ссылочному типу результатом является размер ссылочного типа. При применении к классу результатом является число байтов в объекте этого класса, включая любые заполнение, необходимое для размещения объектов этого типа в массиве. Размер наиболее производного класса должен быть больше нуля (1,8). Результатом применения sizeof к подобъекту базового класса является размер базового класса type.70) При применении к массиву результатом является общее количество байтов в массиве. Это означает, что размер массива из n элементов в n раз больше размера элемента.

Размер ссылочного типа равен размеру его ссылочного типа, поэтому нам нужно задать размер char[N]. Размер этого N * sizeof(char). char является фундаментальным, так как это самый маленький тип, который есть; то есть sizeof(char) - это всегда один. (§5.3.3 / 1) Таким образом, размер этого выражения равен 1 * N, или то, что мы хотели все время: N.

И вот как это работает.


Причина, по которой этот вариант предпочтительнее вашего последнего примера arrayCountof, заключается в том, что результатом sizeof является константное выражение , поэтому его можно использовать в местах, где требуется постоянное выражение. 1072 *

Следует отметить, что в C ++ 0x мы можем получить наш чистый синтаксис без макросов с помощью:

template <typename T, unsigned N>
constexpr size_t arrayCountof(T(&)[N]) {return N;}

* Причина, по которой тип возвращаемого значения функции является ссылкой на массив, а не массив, заключается в том, что вы не можете возвращать массивы. Если бы вы могли, хватит любого выбора.

2 голосов
/ 31 мая 2010

Орен,

Это не ссылка на функцию. Это выражение вызова функции, и оно имеет тип - тип результата. Sizeof может применяться к любому выражению, имеющему тип.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...