C++ Template/Ostream Operator Question

C++ template/ostream operator question

Please look at the error, it says,

friend declaration 'std::ostream&
operator<<(std::ostream&, const
Vector&)' declares a non-template
function|

That means you need to make the operator<< a template function.

So in the class, you've to declare it as:

template<unsigned short m> //<----note this: i.e make it template!
friend std::ostream& operator <<(std::ostream& out, const Vector<m>& v);

Then define it as,

template <unsigned short m>
std::ostream& operator<<(std::ostream& out, const Vector<m>& v) {
out << "(" << v.coords[1] << " - " << v.coords[2] << ")";
return out;
}

overloading operator ostream for template class in c++ not working

As the error message said, the friend declaration declares a non-template operator<<, but it's defined as a template, they don't match.

You can make the friend declaration referring to the operator template, e.g.

// forward declaration
template<class T = int, unsigned int SIZE =2>
class FixedPoint;

// declaration
template<class T,unsigned int SIZE>
std::ostream& operator<<(std::ostream& os ,const FixedPoint<T,SIZE>& price);

template<class T, unsigned int SIZE>
class FixedPoint {
public:
...
friend std::ostream& operator<< <T, SIZE> (std::ostream& os ,const FixedPoint<T, SIZE>& price);
// or just
// friend std::ostream& operator<< <> (std::ostream& os ,const FixedPoint& price);
...
};

// definition
template<class T,unsigned int SIZE>
inline std::ostream& operator<<(std::ostream& os ,const FixedPoint<T,SIZE>& price){
os << price.dollars << "." << price.cents;
return os;
}

Template template parameters and operator (ostream)

std::vector is a template with more than 1 template parameter, so for functioon or operator<< to accept it as a template template parameter, that template template parameter itself needs to accept a variadic number of template parameters:

template<typename T, template<typename ...> typename Tpl_Type>
// ^^^

Without making it variadic, clang and msvc will not compile the code. gcc compiles both versions, but I suspect this is a bug.

Here's a demo.

how to template for operator for ostream

This doesn't need to be a template function.

std::ostream & operator<<(std::ostream & stream, const std::map<std::string, std::string> & some_map)
{
return stream;
}

Edit:

In reference to my comment about writing Java in C++(and sorry if it sounded rude, I didn't intend to be smarmy). Tell me if this doesn't work better for you. Instead of writing a "toString" method in the first place, just overload the operator<< to begin with. The function is nearly identical. Then, you can write a non-member template toString function that will automatically work with all of your classes, like this:

#include <sstream>
#include <string>

template<typename T>
std::string toString(const T & val)
{
std::ostringstream ostr;
ostr << val;
return ostr.str();
}

Edit 2

Here's my alternative if you still insist on doing it your way. Make all of your classes with the toString method inherit from an abstract class with a virtual toString method, then write one operator<< to handle all of them.

class Stringifiable
{
public:
virtual std::string toString() const = 0;
};

std::ostream & operator<<(std::ostream & ostr, const Stringifiable& something)
{
return ostr << something.toString();
}

Now the compiler will choose your overload over templates.

Overloading friend operator for template class

You declare operator<< as returning an ostream&, but there is no return statement at all in the method. Should be:

template <class T, class U>
ostream& operator<<(ostream& out, Pair<T,U>& v)
{
return out << v.val1 << " " << v.val2;
}

Other than that, I have no problems or warnings compiling your code under Visual Studio 2008 with warnings at level 4. Oh, there are the classical linker errors, but that is easily bypassed by moving the template function definition to the class declaration, as explained in the C++ FAQ.

My test code:

#include <iostream>
using namespace std;

template <class T, class U>
class Pair{
public:
Pair(T v1, U v2) : val1(v1), val2(v2){}
~Pair(){}
Pair& operator=(const Pair&);
friend ostream& operator<<(ostream& out, Pair<T,U>& v)
{
return out << v.val1 << " " << v.val2;
}
private:
T val1;
U val2;
};

int main() {
Pair<int, int> a(3, 4);
cout << a;
}

overloading friend operator for template class

This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.

The extrovert

Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.

template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}

The introverts

Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).

This can be done in two ways, the simple way being as @Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:

template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};

In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.

The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:

// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );

// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}

Taking advantage of the extrovert

The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:

namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}

C++ ambigous overload for generic template ostream operator

You get an ambiguous overload for 'operator<< error when the type is std::string, because the templated version in your code has an equal precedence with the one shipped in the ostream header.

You can check that this is the source of your problem by changing your test program with this:

int main() {
std::cout << std::string("There is your problem") << std::endl;
}

And you shall still see the same error.

To solve the problem, you can add an explicit definition of operator<< that will take precedence over the two conflicting templates.

std::ostream& operator<<(std::ostream& os, const std::string& t) {
using std::operator<<;
os << t;
return os;
}

Dynamic Array Template Class: problem with ostream& operator friend function

Although OP seems to have solved her/his problem on its own, I became curious a bit.

OP's problem seems to be to declare a free-standing friend operator<< in a class template. OP's sample code is a bit hard to read, thus I made my own 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;
}

Note: I changed the concept and names a bit as OP's DynamicArray does actually provide something similar like std::vector. I found it reasonable to resemble it a bit closer.

I tried to compile this with

g++ --version ; g++ -std=c++11 -O2 -Wall -pedantic main.cpp && ./a.out

and got the following output:

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

It's funny enough that

  1. the ful answer is already given by g++
  2. and it consists actually of two activities:

make sure the function template has already been declared

and

add <> after the function name here

Concerning the first part, I remembered a similar issue I once found in my answer to SO: Why would a struct need a friend function?.

The second part (add <> after the function name here) is something which raised my attention as I've never seen (nor used) it that way before. So, I'd like to elaborate a bit.

After having inserted the following forward declarations:

// 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>&);

I tried to compile again and got again:

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

exactly as before. Oops!

My first reflex was to change the friend operator to a template friend operator:

template <typename VALUE_>
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE_>&);

and this solved the issue: Live Demo on coliru.


However, this solution has a little flaw which might or might not be annoying: Any operator instance is friend to any VectorT template instance. Actually, this should be constrained to only one operator instance – the one with the same VectorT template instance in signature. This is what g++ actually suggested:

friend std::ostream& operator<< <>(std::ostream&, const VectorT<VALUE>&);

Live Demo on coliru

I wonder why this isn't mentioned in theredfox24's answer – IMHO, this is the actually exciting part of OP's fix.


Finally, I'd like to mention that (in this case) the “whole friend magic” is completely unnecessary. This is what raised my attention first – in the dozens of written output operators (for class templates), I never had any friend issues. This can be easily prevented if the output operator uses public const members of the class exclusively (and I can hardly imagine why they shouldn't be available):

// 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;
}

(Forward declarations and friend operator removed because not needed anymore.)

Live Demo on coliru



Related Topics



Leave a reply



Submit