Reputation: 1
I have a class of shapes, it works on the principle that a shape consists of points (x and y coordinates). But overloading the input and output operators I encountered a problem that could serve as an error.
This is what the compiler says:
In file included from main.cpp:1:
./header/figure.h:17:82: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const Figure<T>&)’ declares a non-template function [-Wnon-template-friend]
17 | friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
| ^
./header/figure.h:17:82: note: (if this is not what you intended, make sure the function template has already been declared and add ‘<>’ after the function name here)
./header/figure.h:18:76: warning: friend declaration ‘std::istream& operator>>(std::istream&, Figure<T>&)’ declares a non-template function [-Wnon-template-friend]
18 | friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);
| ^
/usr/bin/ld: /tmp/ccVeFfI2.o: в функции «main»:
main.cpp:(.text+0xdb): неопределённая ссылка на «operator<<(std::ostream&, Figure<double> const&)»
/usr/bin/ld: main.cpp:(.text+0xf1): неопределённая ссылка на «operator>>(std::istream&, Figure<double>&)»
collect2: error: ld returned 1 exit status
figure.h
#pragma once
#include <ostream>
#include "./dynamicArray.h"
template <class T>
class Figure
{
public:
Figure();
Figure(const DArray<std::pair<T, T>> & points);
Figure(const std::initializer_list<std::pair<T, T>> & coord);
~Figure() noexcept;
friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);
protected:
DArray<std::pair<T, T>> _points;
std::string _name = "unnamed";
};
#include "../src/figure.cpp"
figure.cpp
#include "../header/figure.h"
template <class T>
Figure<T>::Figure() :
_points() {}
template <class T>
Figure<T>::Figure(const DArray<std::pair<T, T>> & points) :
_points(points) {}
template <class T>
Figure<T>::Figure(const std::initializer_list<std::pair<T, T>> & coord) :
_points(coord) {}
template <class T>
Figure<T>::~Figure() noexcept
{
}
template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig)
{
size_t size = fig._points.getSize();
if (size == 0) {
return stream << 0;
}
for (size_t i = 0; i < size; ++i) {
stream << "Point " << i + 1 << "| ";
stream << fig._points[i];
}
return stream;
}
template <class T>
std::istream & operator>>(std::istream & stream, Figure<T> & fig)
{
std::pair<T, T> tmp;
std::cout << "Enter Ox: ";
stream >> tmp.first;
std::cout << "Enter Ox: ";
stream >> tmp.second;
fig._points.pushBack(tmp);
return stream;
}
Upvotes: -3
Views: 111
Reputation: 4249
The simplest way would be inline friend functions. Another thing that we can combine is the template name injection; i.e inside the template you don't need the template argument list, unless you refer to another instantiation with different set of parameters:
template<typename T>
struct test{
T val;
friend auto& operator<<
(std::ostream& os, test const& x){
return os << "value=" << x.val << "\n";
};
};
std::cout << test<int>{1} << test<std::string>{"hello"};
You can start with this simple example and elaborate on it to get the final result.
Upvotes: 0
Reputation: 66961
The problem is that the friend declaration ‘std::ostream& operator<<(std::ostream&, const Figure<T>&)’ declares a non-template function
, but then later you define it as a template function. The solution is to simply declare it as a template function in the first place.
template <class T>
friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
template <class T>
friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);
This feels a little odd to be declaring that Figure<T>
is friending every T
for these overloads, but this is the easy solution.
The "more correct" solution is slightly trickier. The syntax is off the top of my head and probably slightly wrong, but should be something like this
//declare the class as existing as a template type.
template <class T>
class Figure;
//declare the function as existing as a template function.
template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
//define the class
template<class T>
class Figure
{
public:
....
//now you can friend the specific instantiations
friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);
Or the syntax might be friend std::ostream & operator<< <T>(std::ostream & stream, const Figure<T> & fig)
or something. I'm not sure.
Upvotes: 0
Reputation: 409432
The declaration
friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
is not a template declaration. But
template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig)
is a template.
You need to declare the functions as templates from the start:
template<typename U>
friend std::ostream & operator<<(std::ostream & stream, const Figure<U> & fig);
Also please read Why can templates only be implemented in the header file?
Templates can't really be split into separate source and header files like that.
Upvotes: 0