How to define a function template-ed on a container and a type?
For example, overload insertion operator to stream all the elements of a vector, list, or, forward iterator container:
using namespace std;
#include <iostream>
#include <vector>
#include <list>
//...
//...the second argument is a container template-ed on type T
//...
template <typename T,template <typename U> class C>
ostream&
operator<<
(ostream& p_os,const C<T>& p_c)
{
for(typename C<typename T>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
{
p_os.operator<<(*cit);
}
return p_os;
}
int
main
()
{
vector<int> v;
cout << v << endl;
list<int> l;
cout << l << endl;
return 0;
}
This does not compile on g++ 4.9. What is wrong? How is it done?
Why not just pass the container type as template parameter, and find out the element type from it? In your example code you don't even need the element type:
template <typename C>
ostream&
operator<<
(ostream& p_os,const C& p_c)
{
typedef typename C::value_type element_type; // if needed
for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
{
p_os.operator<<(*cit);
}
return p_os;
}
(Although it might be unwise to use this for global functions like this without some enable_if trickery, since it will otherwise match any argument.)
EDIT: You could for example attempt to restrict this to classes with a nested value_type (which all containers have):
template <typename C, typename T = typename C::value_type>
ostream&
operator<<
(ostream& p_os,const C& p_c)
std::vector is a class template that has two template type parameters:
template <class T, class Alloc = allocator<T> >
class vector;
To make your function working with std::vector (and other two-parameter class templates) you can use the following definition:
template <typename T, typename A, template <typename, typename> class C>
// ~~~~~~~~~^ ~~~~~~~^
ostream& operator<<(ostream& p_os, const C<T,A>& p_c)
// ^^
{
for(typename C<T,A>::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
{
p_os.operator<<(*cit);
}
return p_os;
}
or alternatively:
template <typename T, template <typename...> class C>
ostream& operator<<(ostream& p_os, const C<T>& p_c);
Alan Stokes approach works. The code below can stream any container. I just had to add an insertion operator for maps
using namespace std;
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>
#include <set>
#include <deque>
#include <array>
#include <map>
#include <unordered_map>
//...
//...needed for map types which are (key,value) pairs.
//...
template <typename K,typename V>
ostream&
operator<<
(ostream& p_os,const pair<const K,V>& p_v)
{
std::operator<<(p_os,'(');
p_os << p_v.first;
std::operator<<(p_os,',');
p_os << p_v.second;
std::operator<<(p_os,')');
return p_os;
}
template <typename C, typename T = typename C::iterator>
ostream&
operator<<
(ostream& p_os,const C& p_c)
{
for(typename C::const_iterator cit=p_c.begin();cit!=p_c.end();++cit)
{
typename C::value_type v = *cit;
p_os << v;
std::operator<<(p_os,",");
}
return p_os;
}
int
main
()
{
vector<int> v;
for(int i=0;i<4;++i)
{
v.push_back(i);
}
cout << v << endl;
list<int> l;
for(int i=0;i<4;++i)
{
l.push_back(i);
}
cout << l << endl;
forward_list<int> fl = {0,1,2,3};
cout << fl << endl;
set<int> s;
for(int i=0;i<4;++i)
{
s.insert(i);
}
cout << s << endl;
deque<int> d;
for(int i=0;i<4;++i)
{
d.push_back(i);
}
cout << d << endl;
array<int,4> a = {0,1,2,3};
cout << a << endl;
unordered_map<int,int> um;
for(int i=0;i<4;++i)
{
um[i] = i;
}
cout << um << endl;
map<int,int> m;
for(int i=0;i<4;++i)
{
m[i] = i;
}
cout << m << endl;
return 0;
}
Related
The Pairwise class represents a pair with a key:value. I've made a template pair and was having errors trying to run with a key and value input to the class and printing it out.
Given my main:
#include "file_name.h"
int main (){
Pairwise<string, string> example = {{"key", "value"}};
cout << example << endl;
}
And my header file:
#pragma once
#include<iostream>
using std::ostream; using std::cout; using std::endl;
#include<string>
using std::string;
#include<utility>
using std::pair;
#include<sstream>
using std::ostringstream;
template<typename K, typename V>
struct Pairwise{
K first;
V second;
Pairwise() = default;
Pairwise(K, V);
//print out as a string in main
friend ostream& operator<<(ostream &out, const Pairwise &n) {
ostream oss;
string s;
oss << n.first + ":" + n.second; //possible error?
s = oss.str();
out << s;
return out;
}
};
My expected output after running main would be:
key:value
However, I am getting the error:
h:28:11: error: 'std::basic_ostream<_CharT, _Traits> is protected within..."
h:25:59: friend declaration delares a non template function.
You are missing to declare the function as a template that takes Pairwise<K, V>:
header.h:
#ifndef HEADER_H_INCLUDED /* or pragma once */
#define HEADER_H_INCLUDED /* if you like it */
#include <iostream> // or <ostream>
template<typename K, typename V>
class Pairwise { // made it a class so that the
K first; // friend actually makes sense.
V second;
public:
Pairwise() = default;
Pairwise(K first, V second)
: first{ first }, second{ second }
{}
template<typename K, typename V>
friend std::ostream& operator<<(std::ostream &out, Pairwise<K, V> const &p)
{
return out << p.first << ": " << p.second;
}
};
#endif /* HEADER_H_INCLUDED */
source file:
#include <iostream> // the user can't know a random header includes it
#include <string>
#include "header.h"
int main()
{
Pairwise<std::string, std::string> p{ "foo", "bar" };
std::cout << p << '\n';
}
Sidenote: You could also use
{
using Stringpair = Pairwise<std::string, std::string>;
// ...
Stringpair sp{ "foo", "bar" };
}
if you need that more often.
The other errors you got result from confusing std::ostringstream with std::ostream in operator<<().
As you write it, you define the operator as a member function, which is very likely not intended. Divide it like ...
template<typename K, typename V>
struct Pairwise{
K first;
V second;
Pairwise() = default;
Pairwise(K, V);
//print out as a string in main
friend ostream& operator<<(ostream &out, const Pairwise &n);
};
template<typename K, typename V>
ostream& operator<<(ostream &out, const Pairwise<K,V> &n) {
...
return out;
}
And it should work.
BTW: Note that in a struct all members are public by default; so you would be able to access them even in absence of the friend-declaration.
In c++, less is often more...
#pragma once
#include<iostream>
#include<string>
// never do this in a header file:
// using std::ostream;
template<typename K, typename V>
struct Pairwise{
K first;
V second;
Pairwise() = default;
Pairwise(K, V);
//print out as a string in main
friend std::ostream& operator<<(std::ostream &out, const Pairwise &n) {
return out << n.first << ':' << n.second;
}
};
int main (){
using std::cout; using std::endl; using std::string;
Pairwise<string, string> example = {"key", "value"};
cout << example << endl;
}
https://godbolt.org/z/ZUlLTu
i suppose that, if i make operator<< a friend of
a data struct (array by name);
//Forward Declarations
template<typename S, typename T>
struct array;
template<typename U, typename V>
ostream& operator<< (ostream& ous, const array<U, V>& t);
then, i will be able to do something like this; inside the implementation of operator <<
//operator<< is a friend of struct array{} already
template<typename T, typename U>
ostream& operator<< (ostream& os, const array<T, U>& var){
if(var){
/*Error: 'IT' was not declared in this scope*/
for(IT it = var.data.begin(); it != var.data.end(); it++){
/*and i thought i need not redeclare IT before using it
since operator<< is a friend of array already*/
}
}
else{cout << "empty";}
return os;
}
Now, here is array's implementation:
/*explicit (full) specialization of array for <type, char>*/
template<>
template<typename Key>
struct array<Key, char>{
//data members
map<const Key, string> data;
typedef map<const Key, string>::iterator IT;
//member function
friend ostream& operator<< <>(ostream& ous, const array& t);
//other stuff/functions
};
lastly, the compiler is angry when i test-drove it like this;
void Test(){
array<int, char> out;
out[1] = "one"; //Never mind. this has been taken care of
out[2] = "two";
cout << out; //Error: 'IT' was not declared in this scope
}
Question:
what exactly am i doing wrong, or, why can't i dirrectly access and use
IT (a typedef within array), even after i had declared operator << (that requests IT)
as a friend of the array struct ?
Write
for( typename array<T, U>::IT it = var.data.begin(); it != var.data.end(); it++){
And change
typedef map<const Key, string>::iterator IT;
to
typedef typename std::map<const Key, string>::const_iterator IT;
Here is a demonstrative program where instead of the std::map I used std::array for simplicity. I think it can help you.
#include <iostream>
#include <array>
template <typename T, size_t N>
struct A
{
std::array<T, N> a;
typedef typename std::array<T, N>::const_iterator IT;
};
template <typename T, size_t N>
std::ostream & operator <<( std::ostream &os, const A<T, N> &a )
{
for ( typename A<T, N>::IT it = a.a.begin(); it != a.a.end(); ++it ) os << *it << ' ';
return os;
}
int main()
{
A<int, 10> a = { { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } } };
std::cout << a << std::endl;
return 0;
}
The program output is
0 1 2 3 4 5 6 7 8 9
When you're using IT inside your template the compiler looks in the current scope (the operator template) for a declaration of a type named IT.
This fails because you defined the type as part of your array struct.
So in order to use the IT type you need to fully qualify it by using array<T,U>::IT.
Or you could try auto instead if you're using C++11.
I know that I could introduce wrapper(proxy class) to wrap pair and add overloaded << operator, but I I'am wondering why introducing '<<' operator for std namespace like below do not work?
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
namespace std{
ostream& operator<<(ostream& os, pair<int, int>&);
}
std::ostream& std::operator<<(std::ostream& os, std::pair<int, int>& pi){
os << pi.first <<", " << pi.second;
return os;
}
int main(){
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
std::cout << pi.front<<std::endl;
}
This is illegal:
namespace std{
ostream& operator<<(ostream& os, pair<int, int>&);
}
you may only specialise a template class for function in the std namespace for user defined types.
You may not add overloads for user defined types to the std namespace.
This is an overload in namespace std.
anticipating:
but what is the correct way?
This is:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
template<class T>
struct tuple_printer;
template<class T>
auto tuple_print(const T& t) {
return tuple_printer<std::decay_t<T>>(t);
}
template<class T>
struct tuple_printer
{
tuple_printer(const T& t) : _t(t) {}
void operator()(std::ostream& os) const {
os << _t;
}
const T& _t;
};
template<class X, class Y>
struct tuple_printer<std::pair<X, Y>>
{
using arg_type = std::pair<X, Y>;
tuple_printer(const arg_type& t) : _t(t) {}
void operator()(std::ostream& os) const {
os << '(' << tuple_print(_t.first) << ", " << tuple_print(_t.second) << ')';
}
const arg_type& _t;
};
template<class T, class A>
struct tuple_printer<std::vector<T, A>>
{
using arg_type = std::vector<T, A>;
tuple_printer(const arg_type& t) : _t(t) {}
void operator()(std::ostream& os) const {
auto sep = " ";
os << '[';
for (const auto& e : _t) {
os << sep << tuple_print(e);
sep = ", ";
}
os << " ]";
}
const arg_type& _t;
};
template<class T>
std::ostream& operator<<(std::ostream& os, const tuple_printer<T>& tp){
tp(os);
return os;
}
int main(){
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
pi.push_back(std::make_pair(3, 4));
std::cout << tuple_print(pi.front()) << std::endl;
std::cout << tuple_print(pi) << std::endl;
return 0;
}
expected output:
(1, 2)
[ (1, 2), (3, 4) ]
Perfect, eh?
You have a simple typo in your code:
std::vector< std::pair<int, int> > pi;
pi.push_back(std::make_pair(1,2));
std::cout << pi.front<<std::endl;
The last line should read:
std::cout << pi.front() <<std::endl;
note the difference between std::pair<X,Y>::front and std::vector<X>::front().
As a side note, you should probably make your operator<< accept a const pair.
I'm trying to overload << operator for some template class
#include <iostream>
#include <iterator>
#include <map>
template <typename T1, typename T2>
class SampleClass {
public:
SampleClass() = default;
void Add(const T1 &key, const T2 &value)
{
database[key] = value;
}
typename std::map<T1, T2>::const_iterator cbegin() const { return database.cbegin(); }
typename std::map<T1, T2>::const_iterator cend() const { return database.cend(); }
private:
std::map<T1, T2> database;
};
template <typename T1, typename T2>
std::ostream &operator << (std::ostream &os, SampleClass<T1, T2> &rhs)
{
for(auto el = rhs.cbegin(); el != rhs.cend(); el++)
os << el->first << " " << el->second << std::endl;
return os;
}
The error that I'm getting from g++
error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
os << el->first << " " << el->second << std::endl;
I tried also declaring this operator as inline friend function but the result it the same also didn't work. Could anyone explain to me why it happens??
Update:
The thing is the compiler didn't tell me that the cass which i used as the value for the SampleClass didn't have overloaded << operator. I was still looking around in the SampleClass instead of checking other files. But thanks to you that told me that for built in types everything is ok i realized what is needed to be checked. Thank you and sorry for trouble.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Undefined reference error for template method
Splitting templated C++ classes into .hpp/.cpp files--is it possible?
I have this class:
matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <vector>
#include <iostream>
using namespace std;
template <class T> class matrix;
template <typename T> ostream& operator<< (ostream& o, const matrix<T>& m);
template <class T>
class matrix
{
public:
matrix(const int&);
void push(const T&);
void setRows(const int&);
int size();
T& operator ()(const int&, const int&);
friend ostream& operator<< <>(ostream&, const matrix<T>&);
virtual ~matrix();
private:
vector<T> elements_;
int dimension_;
};
#endif // MATRIX_H
#include "matrix.h"
#include <vector>
matrix.cpp
template <class T>
matrix<T>::matrix(const int& n)
{
dimension_ = n;
}
template <class T>
void matrix<T>::push(const T& element)
{
elements_.resize( elements_.size() + 1, element);
}
template <class T>
int matrix<T>::size()
{
return elements_.size()/dimension_;
}
template <class T>
T& matrix<T>::operator ()(const int &n, const int &m)
{
if ((n < 0) || (n > dimension_))
{
cerr << "Row index out of range"
<< endl << endl;
}
if ((m < 0) || (m > dimension_))
{
cerr << "Column index out of range"
<< endl << endl;
}
return elements_[n*dimension_+m];
}
template <class T>
ostream& operator << (ostream& o, const matrix<T>& m)
{
for (int i = 0; i < m.size(); i++)
{
for (int j = 0; j < m.size(); i++)
{
o << m(i,j) << " ";
}
o << endl;
}
return o;
}
template <class T>
matrix<T>::~matrix()
{
//dtor
}
And this program:
#include "matrix.h"
#include <iostream>
using namespace std;
int main()
{
matrix<int> m(3);
m.push(10);
m.push(10);
m.push(11);
m.push(11);
m.push(12);
m.push(12);
m.push(13);
m.push(13);
m.push(10);
cout << m;
cout << "Hello world!" << endl;
return 0;
}
And for EACH. SINGLE. ONE. Of the methods I call, I get a compiler error like this one:
C:\Users...\main.cpp|8|undefined reference to
`matrix::matrix(int const&)'|
I have the matrix.cpp file built. I'm using Code::Blocks, so I have a matrix.o file in the obj folder. That's not the problem.
What is it?