sizeof и передача по ссылке - PullRequest
1 голос
/ 12 августа 2011

Я надеюсь, что кто-то может объяснить следующее поведение.

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

#define array_length(x) ( sizeof(x) / sizeof(x[0]) )

Учитывая, что функция должна работать с любым типом параметра, я должен использовать шаблоны.

Моя первая попытка выглядела примерно так.

template <typename T>
inline size_t
array_length(const T argument) {
    return sizeof(argument) / sizeof(argument[0]);
}

Это не работает, потому что аргумент рассматривается как указатель. Как только T заменен на T и все работает отлично.

Мне интересно, может ли кто-нибудь что-то сказать о том, что происходит на уровне языковой реализации. Например, какая дополнительная информация передается функции, когда ссылки используются в качестве аргументов функции?

Ответы [ 3 ]

5 голосов
/ 12 августа 2011

Давайте рассмотрим два шаблона функций:

template <typename T> void f(T);
template <typename T> void g(T const&);

И скажем, у нас есть массив:

int a[10];

Что происходит, когда мы вызываем f(a);?Поскольку мы явно не предоставляем аргумент в шаблон, выводится аргумент, и компилятор пытается выяснить, что T основано на аргументе.Аргумент имеет тип int[10].T никогда не будет выведен как ссылочный тип, и вы не можете передать массив по значению, поэтому происходит неявное преобразование массива в указатель, и T выводится как int*.

Что происходит, когда мы звоним g(a);?Опять же, вычет аргумента используется, чтобы определить, что такое T.Параметр относится к типу и ссылается на T, поэтому T может быть напрямую выведено в int[10] и преобразование массива в указатель не происходит.

Обратите внимание, что лучшим решением для определения длины массива является явное требование, чтобы шаблон получил массив:

template <typename T, std::size_t N>
std::size_t array_length(T const (&)[N]) { return N; }
2 голосов
/ 12 августа 2011

AFAIK, нет хорошего способа сделать это вне макросов. Вот почему я предпочел бы использовать std::array (новый класс в C ++ 0x, развившийся из boost :: array , который доступен также для "старого" C ++) или std::vector.

1 голос
/ 12 августа 2011

Я знаю, что вам нужна дополнительная техническая информация, но вы можете получить # элементов в массиве без макросов, используя вывод аргумента шаблона:

template<typename T, size_t N>
size_t ElementCount(const T (&x)[N])
{
    return N;
}

Используйте его так:

int x[15];
size_t elements = ElementCount(x);
...