Print pair without introducing wrappers - c++

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.

Related

Overloading the output operator for a template class and a nested class

There is a template class in which a subclass is nested. How to properly overload the output operator to the stream for an external class, which is a template, in which the output operator to the stream is called from a nested class? In my case, compilation errors occur:
no type named 'type' in 'struct std::enable_if<false, void>'
no match for 'operator<<' (operand types are 'std::basic_ostream' and 'Nesting::Nested')
I give my code below.
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class Nesting {
public:
class Nested {
public:
T data;
Nested(const T & data): data(data) {;}
template<typename U>
friend ostream & operator<<(ostream & out, const typename Nesting<U>::Nested & nested);
};
T data;
Nested * pNested;
Nesting(const T & data)
: data(data)
, pNested(new Nested(222)) {;}
template<typename U>
friend ostream & operator<<(ostream & out, const Nesting<U> & nesting);
};
template<typename T>
ostream & operator<<(ostream & out, const typename Nesting<T>::Nested & nested) {
out << "Nested: " << nested.data;
return out;
}
template<typename T>
ostream & operator<<(ostream & out, const Nesting<T> & nesting) {
out << "Nesting: " << nesting.data << ", " << *nesting.pNested << endl;
return out;
}
int main() {
Nesting<int> n(111);
cout << n << endl;
return 0;
}
I think the easiest thing to do is to provide the definition inline with the declaration.
#include <iostream>
#include <memory>
#include <string>
using std::cout;
using std::make_unique;
using std::ostream;
using std::unique_ptr;
template<typename T>
class Nesting {
public:
class Nested {
public:
T data;
Nested(T const& data_): data{data_} {}
friend auto operator<<(ostream& out, Nested const& nested) -> ostream& {
return out << "Nested: " << nested.data;
}
};
T data;
unique_ptr<Nested> pNested;
Nesting(T const& data_)
: data{data_}
, pNested{make_unique<Nested>(222)} {}
friend auto operator<<(ostream& out, Nesting const& nesting) -> ostream& {
return out << "Nesting: " << nesting.data << ", " << *nesting.pNested;
}
};
int main() {
Nesting<int> n(111);
cout << n << "\n";
}
You can add a wrapper to remove the dependant type deduction. Something like:
template<typename T>
struct NestingNested
{
const Nesting<T>::Nested & nested;
template<typename U>
friend std::ostream & operator<<(std::ostream & out, const NestingNested<U> & nested);
};
template<typename T>
std::ostream & operator<<(std::ostream & out, const NestingNested<T> & nested) {
out << "Nested: " << nested.nested.data;
return out;
}
template<typename T>
std::ostream & operator<<(std::ostream & out, const Nesting<T> & nesting) {
out << "Nesting: " << nesting.data << ", " << NestingNested<T>{ *nesting.pNested } << std::endl;
return out;
}
Compiler Explorer

Compile time overloading for ostream operator

I am trying to introduce the overloading mechanism based on rank of the object.
I managed to implement simple example based on other post and it works for simple types:
https://coliru.stacked-crooked.com/a/8129de0ae8a71af1
Now I would like to do sth similar for custom type:
#include <iostream>
#include <type_traits>
#include <sstream>
class Foo {
};
template < class T,
typename std::enable_if <(std::rank<T>::value == 0), int>::type = 0 >
void f(std::ostream& os, const T& value)
{
os << "rank: 0" << std::endl;
}
template< class T,
typename std::enable_if<(std::rank<T>::value == 1), int>::type = 0 >
void f(std::ostream& os, const T& value)
{
os << "rank: 1" << std::endl;
}
template <class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f<decltype(foo)>(os, foo);
return os;
}
int main()
{
Foo foo0;
Foo foo1[5];
//std::cout << std::rank<decltype(foo0)>{} << "\n"; // 0
//std::cout << std::rank<decltype(foo1)>{} << "\n"; // 1
// correct
f<decltype(foo0)>(std::cout, foo0); //prints: rank: 0
f<decltype(foo1)>(std::cout, foo1); //rank: 1
// invalid
std::cout << foo0; //prints: rank: 0
std::cout << foo1; //rank: 0
return 0;
}
https://coliru.stacked-crooked.com/view?id=cf91cec14a111f70
When I am calling functions directly, I am receiving valid results, but when I am doing it by << each time I am getting 0.
Could you explain what I am doing wrong?
You don't need to use decltype here, just use T:
template<class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f< T >(os, foo);
return os;
}
but when using decltype you will have to remove reference from foo type:
template<class T>
std::ostream& operator<<(std::ostream& os, const T& foo)
{
f< std::remove_reference_t< decltype(foo) > >(os, foo);
return os;
}
When template type deduction works T is deduced to be Foo [5] - what rank supports. But when you do decltype(foo), it returns type also with const & - so you have const Foo (&)[4]. rank for that thing just doesn't work.

unable to access friend function in template class

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

Google test and operator<< overload for STL types

Why following code produces compilation error?
#include <iostream>
#include "gtest/gtest.h"
#include <utility>
namespace A {
//overloading operator << for std::pair
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
TEST(TestA, testA) {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
EXPECT_EQ(0, 0) << pair1; //compiles
EXPECT_EQ(0, 0) << pair2; //doesn't compile
}
}
I use Visual Studio 2015. Error text is:
Error C2679 binary '<<': no operator found which takes a right-hand
operand of type 'const std::pair' (or there is no acceptable
conversion) ...\gtest\gtest-message.h 131
How changing user-defined type to built-in type makes a difference?
Upd. Thanks to #Kerrek SB, error is explained. However, now there is another question: How should I overload operator<< for std::pair to be able to use it like in my code?
When one writes something like EXPECT_EQ(e1,e2) << some_pair, the template function
template<typename T>
::testing::Message::operator<<
is being instantiated. Only inside this function user-defined operator<< is being called. Because function resides in another namespace (not in A) user-defined operator<< can't be found.
Solution is easy. Gtest offers function ::testing::PrintToString which accepts STL containers. So the code should be like this (I changed it to make sense):
EXPECT_TRUE(some_predicate(pair2)) << ::testing::PrintToString(pair2);
I took your answer and turned it into a minimal example, removing the dependence on the Google code:
#include <iostream>
namespace A {
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
void test() {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
std::cout << pair1 << std::endl; // compiles
std::cout << pair2 << std::endl; // also compiles
}
}
int main(int argc, char *argv[]) {
A::test();
}
This actually compiles fine for me, with the output
pair:{0, 0}
pair:{0, 0}
So, I cannot reproduce your problem. However, #KerrekSB has identified a good reason for your problem. I suspect the difference might lie in the fact that your code calls operator<< inside TEST, some type of macro defined by the Google Test package, while my more minimal example replaces this with a function in the namespace A, perhaps changing the name lookup?
You need to define operator << for std::pair in std namespace for gtest to find it.
The code as follows compiles fine:
#include <iostream>
#include "gtest/gtest.h"
#include <utility>
namespace std
{
//overloading operator << for std::pair
template<typename T1, typename T2>
ostream& operator<<(ostream& os, const pair<T1, T2>& p) {
return os << "pair:{" << p.first << ", " << p.second << "}";
}
}
namespace A {
struct C {
int x;
};
std::ostream& operator<<(std::ostream& os, const C& c) {
return os << c.x;
}
TEST(TestA, testA) {
std::pair<C, C> pair1;
std::pair<int, int> pair2;
EXPECT_EQ(0, 0) << pair1; //compiles
EXPECT_EQ(0, 0) << pair2; //compiles!
}
}

template classes as template parameters

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