Скомпилирован ли совершенно новый класс для каждого std::array разного размера?
Насколько я понимаю, шаблонирование c++ работает путем компиляции отдельного класса или функции для каждого необходимого типа. Это кажется логичным для классов или функций, которые будут вызываться только для нескольких различных типов/параметров, но для std::array
кажется, что это может привести к тому, что один и тот же класс будет скомпилирован в сотни разных версий.
Я понимаю преимущества std::array
перед массивами в стиле C, но кажется, что использование первого приведет к огромным двоичным размерам по сравнению с последним, если мое предположение верное.
Например, если, скажем, в большой программе мы в конечном итоге используем 99 массивов разных размеров во всем коде, так что мы эффективно имеем:
int arr[1] = { ... }
int arr[2] = { ... }
int arr[...] = { ... }
int arr[99] = { ... }
int arr[100] = { ... }
Или
std::array<int, 1> arr = { ... }
std::array<int, 2> arr = { ... }
std::array<int, ...> arr = { ... }
std::array<int, 99> arr = { ... }
std::array<int, 100> arr = { ... }
Приведет ли пример std::array
к тому, что весь класс и все его функции будут скомпилированы в двоичный файл 99 раз?
Ответы
Ответ 1
Да, новый класс создается шаблоном класса для каждого отдельного набора параметров шаблона.
Но этот класс не обязательно должен существовать в двоичном коде среды выполнения.
Большинство методов короткие, и их следует указывать при использовании. Поэтому они не будут выбрасываться в двоичный файл.
Если вы начнете брать адреса методов и сохранять их, вы начнете раздувать, поскольку вы заставляете каждый отдельный метод существовать.
Например, двоичный генератор раздувания:
template<std::size_t...Ns>
std::function<std::type_info const&()> stupid(std::size_t i, std::index_sequence<Ns...>) {
std::function<std::type_info const&()> retval;
(
((i || (retval = []()->std::type_info const&{
return typeid( std::array<int, Ns> );
})) && i--) && ...
);
return retval;
}
std::function<std::type_info const&()> stupid( std::size_t i ) {
return stupid( i, std::make_index_sequence<100>{} );
}
это требует, чтобы библиотека содержала информацию rtti для 100 различных std::array
с.
Но если вы не делаете такого рода вещи, RTTI не нужен. Так что это не вводится в ваш двоичный файл.
И я могу сделать то же самое с 100 различными массивами.
template<std::size_t...Ns>
std::function<std::type_info const&()> stupid(std::size_t i, std::index_sequence<Ns...>) {
std::function<std::type_info const&()> retval;
(
((i || (retval = []()->std::type_info const&{
return typeid( int[Ns] );
})) && i--) && ...
);
return retval;
}
std::function<std::type_info const&()> stupid( std::size_t i ) {
return stupid( i, std::make_index_sequence<100>{} );
}
"класс" в C++ не такой уж тяжелый предмет, как в других языках ОО. Нет глобального состояния класса, если вы не заставите его существовать.
Ответ 2
Приведет ли пример std::array
к тому, что весь класс и все его функции будут скомпилированы в двоичный файл 99 раз?
Нет, у вас действительно есть один экземпляр класса для каждого параметра...
Но это не будет включать в себя все методы. Будет создан только экземплярный метод.
В вашем случае вы просто используете агрегатную инициализацию, поэтому она идентична.
Ответ 3
Да, std::array<int,1>
будет скомпилирован в другой класс, чем std::array<int,2>
.
Но не волнуйся. поскольку std::array
- это просто тонкая оболочка вокруг массивов c- (int arr[2]
), большинство методов в любом случае будут встроены.
в некотором смысле, std::array<int,1>::operator[]
и std::array<int,2>::operator[]
будут скомпилированы в два разных метода, но эти два метода будут скомпилированы в одни и те же инструкции процессора и будут встроены в функцию вызова, когда оптимизация включена.
Ответ 4
Да, каждая специализация класса будет создаваться для разных аргументов шаблона.
Создание экземпляров специализации класса не приводит к автоматическому созданию определений его функций-членов. Только их декларации создаются. Это не влияет на двоичный код, пока не будут использованы функции.