Объясните, пожалуйста, что происходит
У меня есть вот такое объявление вектора:
vector::vector(size_type n, const string& val = string());
Честно говоря, я вообще не понимаю что тут происходит, кроме того, что я собственно объявляю вектор. Прошу, объясните, пожалуйста!
К тому же выдаёт ещё вот такую ошибку:
[Error] 'template<class _Tp, class _Alloc> class std::vector' used without template arguments
Насколько я понимаю, нужно добавить template <что-то> перед функцией, но я не совсем понимаю что именно.
Спасибо всем, кто объяснит!
Ответы (3 шт):
Ну как я понимаю вектор-то содержит элементы какого-то типа и вы должны указать, что это за тип:
Вектор строк:
vector<string> arr1;
Вектор целых чисел
vector<int> arr2;
Вектор векторов букв
vector<vector<char>> arr3;
Шаблоны - это такие конструкции, в которых не определены типы. (например, если у вас класс для int и для float делает ровно одно и то же, то зачем вам дублировать код? Вы пишите один код на несколько типов.)
Вектор (простыми словами) можно представить как динамический массив. Когда вы определяете массив, вы присваиваете ему тип данных. Например
int* array = new int[128];
А теперь вы пытаетесь создать вектор, но почему то решаете, что тип данных ему не нужен.
vector::vector(size_type n, const string& val = string());
Это просто конструктор класса, без заполнения шаблона его создать не получится.
Вектору тип данных присваивается через шаблон:
// Определение вектора из заголовочного файла vector
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
В качестве первого аргумента передаётся тип данных, который должен хранить вектор. Вторым аргументом распределитель памяти (если второго аргумента нет - используется распределитель по умолчанию).
Например, создадим вектор целых чисел с размером в 10 ячеек:
std::vector<int> vec(10);
Вы можете по необходимости менять его размер с помощью функции resize. Но не забывайте, что перераспределение памяти это дорогостоящая в плане времени выполнения операция, и если количество элементов известно заранее лучше сделать вектор сразу нужно размера (или использовать vector::reserve)
Вы также можете передавать в конструктор значение, которым должен быть инициализирован только что созданный вектор.
Например:
std::vector<int> vec(10, 2);
создаст вектор из 10 элементов, заполненный двойками.
Я здесь рассказал только самое начало, если интересно подробнее: cppreference
Давайте для начала разберем, что такое шаблоны.
Шабло́ны — средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам. В C++ возможно создание шаблонов функций и классов.
Шаблон функции:
template <typename T>
void swap (T& x, T& y){
// тело функции
T temp = x;
x = y;
y = temp;
}
Этот шаблон принимает две ссылки на обобщенный тип T и переставляет значения под ссылками местами. То есть мы можем написать
int main (){
int x = 5;
int y = 12;
std::cout << "x = " << x << "\t y =" << y << endl;
double k =9.4;
double j =45.22;
std::cout << "j = " << j << "\t k =" << k << endl;
swap(x, y);
std::cout << "x = " << x << "\t y =" << y << endl;
swap(k, j);
std::cout << "j = " << j << "\t k =" << k << endl;
return 0;
}
Что происходит на этапе компиляции:
Компилятор : видит, что swap шаблонная. То есть ее можно вызывать от любых переменных с одинаковым типом, но тип не указан.
У компилятора : есть сложная система правил шаблонного вывода, которая в конечном счете подскажет ему какой тип вместо T подставить.
Компилятор : Подставит нужный тип. Вызовет функцию с нужным типом.
Чтобы это быстрее работало можно написать:
int main (){
int x = 5;
int y = 12;
swap <int>(x, y);
return 0;
}
Тут мы явно указываем компилятору, что хотим вызвать void swap (int x, int y);
Можно вызвать нашу swap так :
int main (){
int x = 5;
int y = 12;
swap <double>(x, y);
return 0;
}
Тогда компилятор увидит, что мы хотим вызвать void swap (doublle x, double y);, преобразует int x; int y в double x; double y и вызовет void swap (doublle x, double y); Но явно тип шаблонного параметра в случае с функциями указывают редко.
Теперь рассмотрим шаблон класса (и вспомним, что std::vector тоже шаблонный класс)
template <typename T>
class myArray {
// Поля класса
private :
int m_length;
T* m_data;
// Конструкторы класса
public:
Array()
{
m_length = 0;
m_data = nullptr;
}
Array(int length)
{
m_data = new T[length];
m_length = length;
}
~Array()
{
delete[] m_data;
}
void Erase()
{
delete[] m_data;
// Присваиваем значение nullptr для m_data, чтобы на выходе не получить висячий указатель!
m_data = nullptr;
m_length = 0;
}
T& operator[](int index)
{
assert(index >= 0 && index < m_length);
return m_data[index];
}
// Длина массива всегда является целочисленным значением, она не зависит от типа элементов массива
int getLength(); // определяем метод и шаблон метода getLength() ниже
};
template <typename T> // метод, определенный вне тела класса, нуждается в собственном определении шаблона метода
int Array<T>::getLength() { return m_length; }
}
Теперь мы можем написать:
int main()
{
Array<int> intArray(10);
Array<double> doubleArray(10);
for (int count = 0; count < intArray.getLength(); ++count)
{
intArray[count] = count;
doubleArray[count] = count + 0.5;
}
for (int count = intArray.getLength()-1; count >= 0; --count)
std::cout << intArray[count] << "\t" << doubleArray[count] << '\n';
return 0;
}
Зачем мы явно указывали тип тут?
Array<int> intArray(10);
Array<double> doubleArray(10);
и почему не можем написать
Array array (20);
Потому что на выделение памяти через new (malloc, calloc)
Array(int length)
{
m_data = new T[length];
m_length = length;
}
Эта самая сложная система шаблонного вывода не рассчитана и может работать только с примитивами. Компилятор не может сам определить массив какого типа мы хотим получить и поэтому в сообщении
[Error] 'template<class _Tp, class _Alloc> class Array' used without template arguments
Просит нас явно указать тип.
С вектором то же самое.