Как узнать размер массива, который был передан в шаблонную функцию?

Имею код
template<typename T>
auto func(T arr) -> T {
    cout << "Массив: ";
    for (int i = 0; i < 12; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    cout << "Размер всего массива: " << sizeof(arr)
        << "\nРазмер одного элемента массива: " << sizeof(arr[0])
        << "\nКоличество элементов массива: " << sizeof(arr) / sizeof(arr[0]) << endl;

    return 0;
}


int main()
{
    system("chcp 1251"); system("cls");

    const int N = 12;
    int testArr[N] = { 12, 44, 55, 12, 21, 43, 93, 93, 19, 293, 1939 ,1939};

    func(testArr);
    

    return 0;
}


Если его запустить:
Размер одного элемента массива - верный.
Размер массива в памяти - НЕВЕРНЫЙ. Какой бы размер массива не был - он всегда равен 8.
Соответственно и количество элементов массива тоже неверное.
Почему...?

Реализация через
template<class T, size_t N>
constexpr size_t size(T(&)[N]) { return N; }

В моём случае - не подходит, ибо вызывать функцию, которая определяет размер массива я собираюсь из другой функции, которая так же шаблонная и принимает массив (условно)неизвестного типа данных.
  • Вопрос задан
  • 363 просмотра
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Если коротко, правила автоматического вывода шаблонных аргументов предусматривают вывод полного типа для переданного аргумента при инстанцировании шаблона функции.

Что это означает на практике? В языке есть термин массива статического размера. Частным примером такого массива является строковой литерал. Конкретно у строкового литерала из простых символов тип будет таким: const char[N], где N - это размер памяти под строковой лиерал, в байтах.

Таким образом, если сделать такое объявление шаблона
template< typename TValue >
void Foo( TValue& value ); // (1)

то при инстанцировании как Foo( "Hello" ); аргумент TValue определится как const char[6].
И мы довольно легко можем воспользоваться этим механизмом. Нужно только дать пояснение, как и во что стоит выводить шаблонные аргументы.

Шаблон можно записать вот так:
template< typename TValue, size_t LENGTH >
void Foo( TValue (&value)[ LENGTH ] ); // (2)

В этом случае при инстанцировании как Foo( "Hello" );, аргумент TValue будет выведен как const char, а нетиповой аргумент LENGTH будет выведен в значение 6.
Собственно, все. Размер переданного массива получен.

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

В чем суть модификаторов в параметрах шаблона

Если первый шаблон объявить как
template< typename TValue >
void Foo( TValue value );

то инстанцирование произойдет не для типа массива статического размера, а для указателя на первый элемент этого массива. Это произойдет из-за неявного приведения. Стандарт явно пишет что массив статического размера по значению не передается.
Ссылка позволяет избежать приведения. И опять же, сам массив в функцию передан не будет, только ссылка.
И именно поэтому во втором шаблоне тоже используется ссылка.

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

Параметр такой "другой" функции лучше всего определить универсальной ссылкой и использовать идеальную передачу. Тогда все будет работать правильно.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
mayton2019
@mayton2019
Bigdata Engineer
Передавай вторым параметром size

auto func(T arr, int size) -> T

Или договорись сам с собой что в конце каждого массива будет стоять магическое число (-1 например)
как признак конца.

int testArr[N] = { 12, 44, 55, 12, 21, 43, 93, 93, 19, 293, 1939 ,1939, -1};
Ответ написан
@res2001
Developer, ex-admin
Потому что при передаче в функцию статического/автоматического массива передается указатель, а не сам массив.
8 - размер указателя на 64-битной платформе.
Размер нужно передавать отдельным параметром, как писал mayton2019
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы