Overloading << for vectors works.
Overloading << for custom structs works.
The combination works as well.
But if I use the << operator on a struct with a vector of structs, compilation fails.
I made up a little example to showcase the problem:
#include <iostream>
#include <ostream>
#include <vector>
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
namespace xyz {
struct Item {
int a;
int b;
};
struct Aggregation {
std::vector<Item> items;
};
std::ostream& operator<<(std::ostream& out, const Item& item) {
out << "Item(" << "a = " << item.a << ", " << "b = " << item.b << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
} // namespace xyz
int main() {
xyz::Aggregation agg;
agg.items.emplace_back(xyz::Item{1, 2});
agg.items.emplace_back(xyz::Item{3, 4});
std::cout << agg.items << std::endl; // works: [Item(a = 1, b = 2), Item(a = 3, b = 4)]
std::cout << agg << std::endl; // fails, expected: Aggregation(items = [Item(a = 1, b = 2), Item(a = 3, b = 4))
}
Link to compiler explorer: https://godbolt.org/z/a8dccf
<source>: In function 'std::ostream& xyz::operator<<(std::ostream&, const xyz::Aggregation&)':
<source>:35:41: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::vector<xyz::Item>')
35 | out << "Aggregation(" << "items = " << agg.items << ")";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~ ~~~~~~~~~
| | |
| | const std::vector<xyz::Item>
| std::basic_ostream<char>
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:108:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]'
108 | operator<<(__ostream_type& (*__pf)(__ostream_type&))
| ^~~~~~~~
What am I doing wrong?
In the main function, when you write this line:
std::cout << agg.items << std::endl;
the compiler will look in the global namespace for all overloads of operator<<. The correct overload is chosen via overload resolution, and so the call works.
When you write the similar code here
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
since this code is in namespace xyz, the compiler will first look up the overloads of operator<< in namespace xyz. Once it finds any overloads at all, it will stop looking for additional overloads. However, since the actual operator<< that you want is not in namespace xyz, overload resolution fails, and you get an error.
The fix for this is to simply move the operator<< taking a vector<T> into namespace xyz.
Here's a demo.
If you actually want an operator<< that takes a vector of any type to be accessible from the global scope as well as namespace xyz, then you can define it in the global scope as you have done in your question. Then just bring the operator into xyz, or preferably, into the specific functions in namespace xyz where you need them, like this:
namespace xyz
{
// using ::operator<<; // if you want all of `xyz` to see the global overload
std::ostream& operator<<(std::ostream& out, const Aggregation& agg)
{
using ::operator<<; // if you only want the global overload to be visible in this function
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
// ...
}
Here's a demo that shows how to stream a vector<int> as well as a vector<xyz::Item>.
Thanks to #NathanPierson for pointing out that the using declaration can be local to the functions where it's needed, instead of polluting the entirety of namespace xyz.
I ran over a similar issue again with the fmt library (https://github.com/fmtlib/fmt/issues/2093). Another working solution seems to be adding operator<< overloading for std containers directly to namespace std:
namespace std {
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
} // namespace std
Link to compiler explorer: https://godbolt.org/z/o7c9WP
I feel bad about adding something to namespace std though. Any thoughts on this?
Related
I have a variable x.
It may either be of type char, uint8_t or std::string.
I wish to output the number (not character), using the same expression involving std::cout. This is because I use this expression in generated code.
At code-generation-time, I currently don't know if x will be char, uint8_t or std::string.
std::cout << x << std::endl does not work if x is of type char, since it will output the character and not the number.
std::cout << +x << std::endl does not work if x is of type std::string.
std::cout << (typeid(x) == typeid(uint8_t) || typeid(x) == typeid(char) ? +x : x) << std::endl does not work if x is of type std::string.
std::cout << (typeid(x) == typeid(uint8_t) || typeid(x) == typeid(char) ? static_cast<int>(x) : x) << std::endl does not work if x is of type std::string.
I am aware that std::cout can be configured in various ways by piping std::hex or std::boolalpha, but I know of no possible way to configure std::cout to output a char as a number, without casting the char first.
Is there a way to use reflection, operator overloading, templates or something else
so that one can have a single unified statement for outputting x, as a number?
For example, if x is 65 with type char, the desired output is 65, not A.
Just format a helper and specialize the versions you want to customize appropriately. For example:
#include <iostream>
template <typename T>
struct formatter {
T const& value;
};
template <typename T>
formatter<T> format(T const& value) {
return formatter<T>{value};
}
template <typename T>
std::ostream& operator<< (std::ostream& out, formatter<T> const& v) {
return out << v.value;
}
std::ostream& operator<< (std::ostream& out, formatter<char> const& v) {
return out << int(v.value);
}
template <std::size_t N>
std::ostream& operator<< (std::ostream& out, formatter<char[N]> const& v) {
return out << '\'' << v.value << '\'';
}
int main() {
std::cout << "char=" << format('c') << " "
<< "int=" << format(17) << " "
<< "string=" << format("foo") << " "
<< "\n";
}
I guess you are working within generic context. So your basic problem is that you need static dispatch. The trigraph operator ? : does not provide this. it is evaluated at run time and will always invoke the same operator<<.
So you have two options:
use a helper class with partial specialization.
use static if. I.e.:
if constexpr (std::is_integral<decltype(x)>::value)
std::cout << static_cast<int>(x) << std::endl;
else
std::cout << x << std::endl;
The latter requires C++17.
This solution worked for me. It outputs the char as a number by using the output function together with template specialization and if constexpr:
#include <cstdint>
#include <iostream>
#include <string>
using namespace std::string_literals;
template <typename T> void output(std::ostream& out, T x)
{
if constexpr (std::is_integral<decltype(x)>::value) {
out << static_cast<int>(x);
} else {
out << x;
}
}
int main()
{
char x = 65;
uint8_t y = 66;
std::string z = "hi"s;
// output: A
std::cout << x << std::endl;
// output: 65
output(std::cout, x);
std::cout << std::endl;
// output: B
std::cout << y << std::endl;
// output: 66
output(std::cout, y);
std::cout << std::endl;
// output: "hi"
output(std::cout, z);
std::cout << std::endl;
return 0;
}
Thanks to Dietmar Kühl and Marcel for the helpful answers.
I'm trying to understand why I can use an ostream_iterator for Edge1 but not for Edge in the following code:
#include <fstream>
#include <iostream> // for std::cout
#include <utility> // for std::pair
using VertexName = uint32_t;
using Edge = std::pair<VertexName, VertexName>;
struct Edge1 : public Edge {
Edge1(VertexName x, VertexName y) : Edge(x,y) {};
};
std::ostream&
operator<<(std::ostream& os, const Edge& e) {
os << "(" << e.first << ", " << e.second << ")";
return os;
}
int main(int,char*[])
{
auto e1 = Edge(4,5);
auto e2 = Edge1(5,6);
std::cout << e1 << ", " << e2 << std::endl;
auto it = std::ostream_iterator<Edge1>(std::cout, ", ");
//*it++ = e1;
*it++ = e2;
}
```
Although I can print out both e1 and e2 using the overloaded operator<<(std::stream& os, const Edge& e) function, I get the following error from clang-5.0 if I try to change the ostream_iterator to std::stream_iterator<Edge>(std::cout, ", ") and uncomment the *it++ = e1 line.
error: invalid operands to binary expression ('ostream_type' (aka 'basic_ostream<char, std::__1::char_traits<char> >') and 'const std::__1::pair<unsigned int, unsigned int>')
*__out_stream_ << __value_;
~~~~~~~~~~~~~~ ^ ~~~~~~~~
/main.cpp:25:11: note: in instantiation of member function 'std::__1::ostream_iterator<std::__1::pair<unsigned int, unsigned int>, char, std::__1::char_traits<char> >::operator=' requested here
*it++ = e1;
Edge is not a type it's a type alias of std::pair.
And of course ADL is not finding the overload of operator<< for Edge because it's defined in the wrong namespace... and you're not allowed to inject an overload in the std namespace.
The workaround is:
#include <fstream>
#include <iostream> // for std::cout
#include <utility> // for std::pair
#include <iterator> // for std::ostream_iterator
using VertexName = uint32_t;
// Edge is now a type, in the global namespace...
struct Edge : std::pair<VertexName, VertexName> {
using std::pair<VertexName, VertexName>::pair;
};
struct Edge1 : public Edge {
Edge1(VertexName x, VertexName y) : Edge(x,y) {};
};
// ...and this operator<< is defined in the global namespace so
// ADL will now find it.
std::ostream&
operator<<(std::ostream& os, const Edge& e) {
os << "(" << e.first << ", " << e.second << ")";
return os;
}
int main(int,char*[])
{
auto e1 = Edge(4,5);
auto e2 = Edge1(5,6);
std::cout << e1 << ", " << e2 << std::endl;
auto it = std::ostream_iterator<Edge>(std::cout, ", ");
*it++ = e1;
*it++ = e2;
}
Edit: Definition of class TF:
class TF {
std::vector<V4f> waypoints;
std::vector<int> densityWaypoints;
public:
std::size_t size() const { return waypoints.size(); }
friend std::ostream& operator<<(std::ostream& str, const TF& tf);
friend std::fstream& operator<<(std::fstream& str, const TF& tf);
// methods here
};
The question may steam from the fact that I don't understand streams, so that's probably a precondition.
Is it somehow possible to overload operator<<(std::ostream, T) so that when invoked in order to display the data structure on screen, it uses one overload, and when the data structure is written to a file, another one is used? Something like this probably:
std::ostream& operator<<(std::ostream& str, const TF& tf) {
for (std::size_t i = 0; i != tf.waypoints.size(); ++i) {
str << " { "
<< tf.densityWaypoints[i] << " : "
<< tf.waypoints[i][3] << " : "
<< tf.waypoints[i][0] << " , "
<< tf.waypoints[i][1] << " , "
<< tf.waypoints[i][2]
<< " } ";
}
str << "\n";
return str;
}
std::fstream& operator<<(std::fstream& str, const TF& tf) {
str << (int)tf.size();
for (std::size_t i = 0; i != tf.waypoints.size(); ++i) {
str << tf.densityWaypoints[i]
<< tf.waypoints[i][0]
<< tf.waypoints[i][1]
<< tf.waypoints[i][2]
<< tf.waypoints[i][3];
}
This doesn't compile with a strange error (I may be tired):
error: no match for ‘operator<<’ (operand types are ‘std::fstream {aka std::basic_fstream}’ and ‘int’)
The error occurs when I add the second operator<<() overload. The first one works fine. Tried both std::ofstream and std::fstream to the same result.
But I'm not sure if it's going to work either. Sure it's possible to define a function like int writeTF(std:fstream& str, const TF&tf), but that doesn't look C++ enough to me, not to mention the strange error that will potentially appear here, too.
I've seen code comparing the ostream's address to that of cout. I have mixed feelings about it, but it certainly worked:
std::ostream& operator<<(std::ostream& o, Foo const&)
{
if(&o == &std::cout) {
return o << "cout";
} else {
return o << "not_cout";
}
}
demo
Note that cout outputs to standard output, it's not the same thing as "the screen".
I have the following code to pretty-print generic vectors -:
// print a vector
template<typename T1>
std::ostream& operator <<( std::ostream& out, const std::vector<T1>& object )
{
out << "[";
if ( !object.empty() )
{
std::copy( object.begin(), --object.end(), std::ostream_iterator<T1>( out, ", " ) );
out << *--object.end(); // print the last element separately to avoid the extra characters following it.
}
out << "]";
return out;
}
I am getting a compiler error if I try to print a nested vector from it.
int main()
{
vector<vector<int> > a;
vector<int> b;
// cout << b ; // Works fine for this
cout << a; // Compiler error
}
I am using GCC 4.9.2 with the -std=c++14 flag.
The error message given by the compiler is -:
no match for 'operator<<' (operand types are
'std::ostream_iterator<std::vector<int>, char, std::char_traits<char>::ostream_type {aka std::basic_ostream<char>}' and 'const std::vector<int>')
std::copy( object.begin(), --object.end(), std::ostream_iterator<T1>( out, ", " ) );
You are using copy to ostream iterator which is not defined for vector of std::vector<>. One work around is to implement operator << in terms of operator << of children.
if ( !object.empty() )
{
//std::copy( object.begin(), --object.end(), std::ostream_iterator<T1>( out, ", " ) );
for(typename std::vector<T1>::const_iterator t = object.begin(); t != object.end() - 1; ++t) {
out << *t << ", ";
}
out << *--object.end(); // print the last element separately to avoid the extra characters following it.
}
Live example here
This method will not work for the same reason for std::vector<my_type> if opeartor << is not defined for class my_type
Just using a normal forloop instead of std::copy would solve this issue. As #Mohit suggested, the ostream iterator is not defined for nested vectors.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>
using namespace std;
// print a vector
template<typename T1>
std::ostream& operator <<( std::ostream& out, const std::vector<T1>& object )
{
out << "[";
if ( !object.empty() )
{
for(typename std::vector<T1>::const_iterator
iter = object.begin();
iter != --object.end();
++iter) {
out << *iter << ", ";
}
out << *--object.end();
}
out << "]";
return out;
}
int main() {
std::vector<std::vector<int> > a;
std::vector<int> b;
b.push_back(1);
b.push_back(2);
std::vector<int> c;
c.push_back(3);
c.push_back(4);
std::cout << b << std::endl;
std::cout << c << std::endl;
a.push_back(b);
a.push_back(c);
cout << a; // Compiler error
return 0;
}
Output would look like this:
[1, 2]
[3, 4]
[[1, 2], [3, 4]]
Everything was fine until I moved my objects to a namespace. And now the compiler claims that my Color attributes are private.
I thought the whole point of friends was to share encapsulated information with those a class befriends.
Color.h
friend ostream & operator << (ostream& output, const st::Color& color);
Color.cpp:
ostream & operator <<(ostream& output, const st::Color& color) {
output << "Colors:\nalpha\t: " << color.a << "\nred\t: " << color.r << "\ngreen\t: " << color.g
<< "\nblue\t: " << color.b << "\nvalue\t: " << color.color();
return output;
}
error:
Color.h||In function 'std::ostream& operator<<(std::ostream&, const st::Color&)':|
Color.h|52|error: 'unsigned char st::Color::a' is private|
Color.cpp|15|error: within this context|
Color.h|49|error: 'unsigned char st::Color::r' is private|
Color.cpp|15|error: within this context|
Color.h|51|error: 'unsigned char st::Color::g' is private|
Color.cpp|15|error: within this context|
Color.h|50|error: 'unsigned char st::Color::b' is private|
Color.cpp|16|error: within this context|
||=== Build finished: 8 errors, 0 warnings (0 minutes, 1 seconds) ===|
So what is the deal?
I'm using Code::Blocks as my IDE. And it won't even show any properties or methods when I use the dot operator on the "color" parameter. This is obviously a sign of something going wrong...somewhere.
I've taken the friend operator overloading out and it compiles just fine. No error elsewhere.
What gives?
It's declared as follows:
namespace st{
class Color {
friend ostream & operator << (ostream& output, const st::Color& color);
public:
....
private:
.....
};
};
Edit:
In my CPP I've now done this:
namespace st{
ostream & st::operator <<(ostream& output, const st::Color& color) {
output << "Colors:\nalpha\t: " << color.a << "\nred\t: " << color.r << "\ngreen\t: " << color.g
<< "\nblue\t: " << color.b << "\nvalue\t: " << color.color();
return output;
}
}
st::Color::Color() {
reset();
}
st::Color::Color(const Color& orig) {
a = orig.a;
r = orig.r;
g = orig.g;
b = orig.b;
}
void st::Color::reset() {
a = 0;
r = 0;
g = 0;
b = 0;
}
... etc
}
No compile errors, but is it normal for such a situation to use the namespace again in the header? Or is this completely off from what I should be doing?
Edit:
#Rob thanks for your input as well!
You need to declare and define your operators in the same namespace as the object as well. They will still be found through Argument-Dependent-Lookup.
A usual implementation will look like this:
/// header file
namespace foo {
class A
{
public:
A();
private:
int x_;
friend std::ostream& operator<<(std::ostream& o, const A& a);
};
std::ostream& operator<<(std::ostream& o, const A& a);
} // foo
// cpp file
namespace foo {
A::A() : x_(23) {}
std::ostream& operator<<(std::ostream& o, const A& a){
return o << "A: " << a.x_;
}
} // foo
int main()
{
foo::A a;
std::cout << a << std::endl;
return 0;
}
Edit
It seems that you are not declarin your operator<< in the namespace and are also defining it outside of the namespace. I've adjusted the code.
You need to qualify your operator with the namespace as well. It is a function signature declared in the name space so to access its symbol you need to prefix it with the namespace.
Try it like this:
namespace st {
ostream & operator <<(ostream& output, const Color & color) {
output << "Colors:\nalpha\t: " << color.a
<< "\nred\t: " << color.r
<< "\ngreen\t: " << color.g
<< "\nblue\t: " << color.b
<< "\nvalue\t: " << color.color();
return output;
}
}