Хотя ОП, похоже, решил свою проблему самостоятельно, мне стало немного любопытно.
Проблема OP в том, чтобы объявить автономный friend operator<<
в шаблоне класса. Пример кода OP немного сложен для чтения, поэтому я сделал свой собственный MCVE :
#include <iostream>
#include <exception>
#include <algorithm>
// template class for dynamic array
template <typename VALUE>
class VectorT {
private:
VALUE *_values;
size_t _capacity;
size_t _size;
public:
VectorT(): _values(nullptr), _capacity(0), _size(0) { }
~VectorT() { delete[] _values; }
VectorT(const VectorT &vec); /// @todo
VectorT& operator=(const VectorT &vec); /// @todo
size_t capacity() const { return _capacity; }
size_t size() const { return _size; }
VALUE& operator[](size_t i) { return _values[i]; }
const VALUE& operator[](size_t i) const { return _values[i]; }
void push_back(const VALUE &value)
{
if (_size == _capacity) { // realloc necessary
const size_t capacity = std::max(2 * _capacity, (size_t)1);
VALUE *const values = new VALUE[capacity];
if (!values) throw std::bad_array_new_length();
std::move(_values, _values + _size, values);
delete[] _values;
_values = values; _capacity = capacity;
}
_values[_size++] = value;
}
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
};
// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
const char *sep = "";
for (size_t i = 0; i < vec._size; ++i) {
out << sep << vec[i];
sep = ", ";
}
return out;
}
// test
int main()
{
VectorT<int> vec;
// populate vec
vec.push_back(20);
vec.push_back(12);
vec.push_back(13);
vec.push_back(45);
vec.push_back(78);
// test output operator
std::cout << vec << '\n';
// done
return 0;
}
Примечание: я немного изменил концепцию и названия, так как OP DynamicArray
действительно предоставляет что-то похожее на std::vector
. Я посчитал разумным напоминать об этом немного ближе.
Я пытался скомпилировать это с
g++ --version ; g++ -std=c++11 -O2 -Wall -pedantic main.cpp && ./a.out
и получил следующий вывод:
g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
main.cpp:38:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
^
main.cpp:38:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
/tmp/ccvsl6kw.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status
Достаточно смешно, что
- ful ответ уже дан
g++
- и состоит из двух действий:
убедитесь, что шаблон функции уже объявлен
и
добавить <> после имени функции здесь
Что касается первой части, я вспомнил похожую проблему, которую однажды обнаружил в своем ответе на SO: Зачем структуре нужна функция друга? .
Вторая часть ( add <> после имени функции здесь ) - это то, что привлекло мое внимание, поскольку я никогда не видел (и не использовал) это таким образом раньше. Итак, я хотел бы немного уточнить.
После вставки следующих предварительных деклараций:
// forward declaration of VectorT
template <typename VALUE>
class VectorT;
// prototyping of output stream operator
template <typename VALUE>
std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
Я попытался снова скомпилировать и получил снова:
main.cpp:46:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
^
main.cpp:46:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
/tmp/ccXLnkbV.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status
точно так же, как и раньше. Oops!
Мой первый рефлекс состоял в том, чтобы заменить friend operator
на template friend operator
:
template <typename VALUE_>
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE_>&);
и это решило проблему: Живая демоверсия на coliru .
Однако, у этого решения есть небольшой недостаток, который может раздражать или не раздражать: любой экземпляр оператора дружит с любым экземпляром шаблона VectorT
. На самом деле, это должно быть ограничено только одним экземпляром оператора & ndash; один с тем же экземпляром шаблона VectorT
в подписи. Вот что g++
на самом деле предложил:
friend std::ostream& operator<< <>(std::ostream&, const VectorT<VALUE>&);
Демонстрация в реальном времени на coliru
Интересно, почему это не упоминается в ответе theredfox24 & ndash; ИМХО, это действительно захватывающая часть исправления ОП.
Наконец, я хотел бы отметить, что (в данном случае) & ldquo; целое friend
волшебство & rdquo; совершенно ненужно. Это то, что привлекло мое внимание в первую очередь - в десятках написанных операторов вывода (для шаблонов классов) у меня никогда не было проблем с friend
. Это можно легко предотвратить, если оператор вывода использует public
const
членов исключительно class
(и я с трудом могу представить, почему они не должны быть доступны):
// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
const char *sep = "";
for (size_t i = 0; i < vec.size(); ++i) {
out << sep << vec[i];
sep = ", ";
}
return out;
}
(Перенаправленные объявления и оператор friend
удалены, поскольку больше не нужны.)
Демонстрация в реальном времени на coliru