Error when inserting string << overload C++ - c++

I am trying to overload the << operator on a class in C++. Whenever I insert a normal string, like the " " into the output stream I get compilation errors that I cannot make sense of. I have done this once before with no problems, so I am very confused.
friend std::ostream& operator<<(std::ostream& out, Variable v);
std::ostream& operator<<(std::ostream& out, Variable v) {
out << v.type;
out << " ";
out << v.name;
return out;
}
And here is the output:
src/Variable.cpp: In function 'std::ostream& operator<<(std::ostream&, Variable)':
src/Variable.cpp:35:9: error: no match for 'operator<<' in 'out << " "'
src/Variable.cpp:35:9: note: candidates are:
src/Variable.cpp:33:15: note: std::ostream& operator<<(std::ostream&, Variable)
src/Variable.cpp:33:15: note: no known conversion for argument 2 from 'const char [2]' to 'Variable'
In file included from /usr/local/Cellar/gcc/4.7.0/gcc/lib/gcc/x86_64-apple-darwin10.8.0/4.7.0/../../../../include/c++/4.7.0/string:54:0,
from src/../inc/Variable.h:4,
from src/Variable.cpp:1:
/usr/local/Cellar/gcc/4.7.0/gcc/lib/gcc/x86_64-apple-darwin10.8.0/4.7.0/../../../../include/c++/4.7.0/bits/basic_string.h:2750:5: note: template<class _CharT, class _Traits, class _Alloc> std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::basic_string<_CharT, _Traits, _Alloc>&)
/usr/local/Cellar/gcc/4.7.0/gcc/lib/gcc/x86_64-apple-darwin10.8.0/4.7.0/../../../../include/c++/4.7.0/bits/basic_string.h:2750:5: note: template argument deduction/substitution failed:
src/Variable.cpp:35:9: note: mismatched types 'const std::basic_string<_CharT, _Traits, _Alloc>' and 'const char [2]'
make: *** [bin/Variable.o] Error 1

Derp. I did not include iostream. However, this does not make much sense to me... since it worked whenever I did not add a string to the ostream. I would think that the compiler would not be able to find ostream at all, and would complain about that

#include <utility>
#include <iostream>
template <typename T1, typename T2>
std::ostream& operator<< (std::ostream& out, const std::pair<T1, T2>& v)
{
out << v.first;
out << " ";
out << v.second << std::endl;
return out;
}
int main()
{
std::pair<int, int> a = std::make_pair(12, 124);
std::cout << a << std::endl;
return EXIT_SUCCESS;
}
Its an example how to declare and implement an operator <<

I will show your solution in code
#include <iostream>
#include <string>
/* our custom class */
class Developer
{
public:
Developer(const std::string& name, int age) :
m_name(name),
m_age(age)
{}
const std::string& name() const { return m_name; }
int age() const { return m_age; }
private:
std::string m_name;
int m_age;
};
/* overloaded operator<< for output of custom class Developer */
std::ostream& operator<< (std::ostream& stream, const Developer& developer)
{
stream << "Developer name:\t" << developer.name() << std::endl;
stream << "Developer age:\t" << developer.age() << std::endl;
return stream;
}
/* test custom operator<< for class Developer */
int main(int argc, const char** argv)
{
Developer max("Maxim", 23);
std::cout << max;
return 0;
}

Related

How to check the type of the parameter sent in a template?

I'm trying to create a simple logger which logs different messages to disk. The logger is a simple class of which I've overridden the << operator and, in order to save the messages, I send an enum value signaling to save what has come so far. basically flush everything to the disk.
This is how I intend on using this logger:
Logging::Logger << "First " << 255 << "Second" << exc.what() << Logging::Save;
As you can see, since I need a way to distinuish between controling arguments such as Logging::Save and other normal arguments, I need to know what I'm dealing with.
For getting the type of argument sent, I used std::is_same. However, it seems for some reasons the check doesn't work and I get the following error :
In instantiation of 'Logging::Log& Logging::Log::operator<<(const T&) [with T = char [19]]':
104:35: required from here
47:31: error: no match for 'operator==' (operand types are 'const char [19]' and 'Logging::Mode')
In instantiation of 'Logging::Log& Logging::Log::operator<<(const T&) [with T = Logging::Mode]':
104:67: required from here
57:28: error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'
In file included from /usr/include/c++/4.9/iostream:39:0,
from 10:
/usr/include/c++/4.9/ostream:602:5: note: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Logging::Mode]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
This is what I have come up so far :
#include <iostream>
#include <fstream>
#include <sstream>
#include <typeinfo>
#include <type_traits>
namespace Logging
{
enum class Mode
{
Save = 0,
Load = 1
};
static constexpr Mode Save = Mode::Save;
class Log
{
private:
std::stringstream stream;
bool isNew = true;
bool displayResults = false;
public:
template <typename T>
Log& operator<<(const T& value)
{
if (isNew)
{
stream << "start : ";
isNew = false;
}
if (std::is_same<T, Logging::Mode>::value)
{
if (value == Mode::Save)
Save();
if (displayResults)
std::cout << std::endl;
isNew = true;
}
else
{
stream << value;
}
if (displayResults)
std::cout << stream.str();
return *this;
}
void Save(std::string logFilename= "Log.txt")
{
isNew = true;
std::ofstream logFile;
logFilename = (logFilename == "") ? "Log.txt" : logFilename;
logFile.open(logFilename, std::ios::out | std::ios::app);
logFile << this->Get();
this->stream.clear();
this->stream.str("");
}
Log& Display(bool status)
{
displayResults = status;
return *this;
}
std::string Get() const
{
return this->stream.str();
}
friend std::ostream& operator<<(std::ostream& os, const Log& log);
};
std::ostream& operator<<(std::ostream& os, const Log& log)
{
os << log.Get();
return os;
}
Log Logger;
}
int main()
{
Logging::Logger.Display(true)<< "What is your name?" <<Logging::Save;
}
What am I missing here?
You have it almost correct, if constexpr(std::is_same<T, Logging::Mode>::value) is what you want in C++17.
Without constexpr the body of the if statement is compiled no matter the condition, this did generate the error about comparing arrays with enums even though the branch is never taken in such case.
'operator==' (operand types are 'const char [19]' and 'Logging::Mode').
Consider using universal references template<typename T> T&& and perfect forwarding for passing value around. It is good practice.
In your case a simple overloaded helper method works too:
template <typename T>
Log& operator<<(T&& value)
{
if (isNew)
{
stream << "start : ";
isNew = false;
}
helper(std::forward<T>(value));
if (displayResults)
std::cout << stream.str();
return *this;
}
template<typename T>
void helper(T&& value){
stream << std::forward<T>(value);
}
// Perfect forwarding requires that the enum is taken by value,
// otherwise the template is a better match in some cases.
// - Or you would have to write both const& and && versions.
void helper(Logging::Mode value){
if (value == Mode::Save)
Save();
if (displayResults)
std::cout << std::endl;
isNew = true;
}

cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’

There have been a couple posts on this subject, but I think this is one of the simplest examples, and hopefully it will clarify some things about cout and initialization.
So this works:
class A {
public:
std::ostream& operator<< (std::ostream& os) {
return os;
}
};
class B {
std::ostream& operator<< (std::ostream& os) {
A a(); // <-- LOOK
std::cout << a;
return os;
}
};
But if I simply A a() to A a:
class A {
public:
std::ostream& operator<< (std::ostream& os) {
return os;
}
};
class B {
std::ostream& operator<< (std::ostream& os) {
A a; // <-- LOOK
std::cout << a;
return os;
}
};
It throws:
nvcc main.cpp util.cpp -o main -lcublas -std=c++11
In file included from main.cpp:9:0:
cout-test.hpp: In member function ‘std::ostream& B::operator<<(std::ostream&)’:
cout-test.hpp:21:20: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
std::cout << a;
^
In file included from /usr/include/c++/4.8/iostream:39:0,
from main.cpp:5:
/usr/include/c++/4.8/ostream:602:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = A]’
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
make: *** [main] Error 1
I get the same error if I make A a a class member:
class B {
A a; // <-- LOOK
std::ostream& operator<< (std::ostream& os) {
std::cout << a;
return os;
}
};
What gives?
First Case
A a();
does not construct an object. It declares a function. This parsing problem is known as The Most Vexing Parse.
A a();
std::cout << a;
works because a is converted to a bool in this case. See Why does pointer to int convert to void* but pointer to function convert to bool? why that works.
Second Case
A a;
std::cout << a;
does not work because of the way you have defined the operator<< function. You'll have to use
A a;
a << std::cout;
The operator<< function needs to be a non-member function in order to use:
A a;
std::cout << a;
See my answer to another SO post to understand why.

Overloading << and >> for STL map: "cannot bind lvalue"

I am trying to save an STL map containing custom objects to a file, using fstream. I am doing this using << and >> operator overloading.
#include <iostream>
#include <string>
#include <map>
#include <fstream>
#define DELIM '/'
struct Point{
public:
int x, y;
};
//operator overloads for point
std::istream& operator>>(std::istream& is, Point& p){
std::string input;
std::getline(is, input, DELIM);
p.x = std::stoi(input);
std::getline(is, input, DELIM);
p.y = std::stoi(input);
return is;
}
std::ostream& operator<<(std::ostream& os, Point& p){
os << p.x << DELIM << p.y << DELIM;
return os;
}
//operator overloads for map<string, point>
std::istream& operator>>(std::istream& is, std::map<std::string, Point>& m){
std::string input;
std::getline(is, input, DELIM);
int map_size = std::stoi(input);
for(int i = 0; i < map_size; i++){
std::getline(is, input, DELIM);
Point p; is >> p;
m[input] = p;
}
return is;
}
std::ostream& operator<<(std::ostream& os, std::map<std::string, Point>& m){
os << m.size() << DELIM;
for(const auto& pair : m){
os << pair.first << DELIM;
os << pair.second;
}
return os;
}
int main(){
Point p1;
p1.x = 1; p1.y = 2;
Point p2;
p2.x = 100; p2.y = 150;
std::map<std::string, Point> map;
map["p1"] = p1;
map["p2"] = p2;
return 0;
}
When I try to compile this file I get the following error:
test.cpp: In function 'std::ostream& operator<<(std::ostream&, std::map<std::basic_string<char>, Point>&)':
test.cpp:44:14: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
os << pair.second;
Any idea how to fix this? I ran into this problem earlier with templates, however there are no templates used here. Other answers on SO also involved templates and did not help me. An explanation of why this is happening would be appreciated too!
The command I used to compile: (MinGW, gcc 4.8.1)
g++ test.cpp -o test -std=c++11 -Wall
The full error message:
test.cpp: In function 'std::ostream& operator<<(std::ostream&, std::map<std::basic_string<char>, Point>&)':
test.cpp:44:14: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
os << pair.second;
^
In file included from c:\mingw\lib\gcc\x86_64-w64-mingw32\4.8.1\include\c++\iostream:39:0,
from test.cpp:1:
c:\mingw\lib\gcc\x86_64-w64-mingw32\4.8.1\include\c++\ostream:602:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Point]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
Thank you!!
Here
for(const auto& pair : m){
os << pair.first << DELIM;
os << pair.second;
}
pair is const, so pair.second will be const Point too, which can't match the parameter type Point& of operator<<.
Change the parameter type of Point for operator<< to const reference:
std::ostream& operator<<(std::ostream& os, const Point& p){
~~~~~
os << p.x << DELIM << p.y << DELIM;
return os;
}
Mostly, the argument passed to operator<< is not expected to be changed, so it's good practice to declare the type to const& for both operator<<.
std::ostream& operator<<(std::ostream& os, const Point& p){
std::ostream& operator<<(std::ostream& os, const std::map<std::string, Point>& m){
The argument passed to the overloaded << operator should be a constant reference, not a mutable reference.

Trying to print vector of objects with overloading?

I'm working on a hash table program involving linear probing. I'm trying to print out the vector of Symbol class objects but I'm running into two errors every time I try to do it. I've posted these errors below:
LinearProbing.h: In instantiation of 'void HashTable<HashedObj>::print() [with HashedObj = Symbol]':
Driver.cpp:79:21: required from here
LinearProbing.h:82:4: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
In file included from /opt/local/include/gcc47/c++/iostream:40:0,
from Driver.cpp:1:
/opt/local/include/gcc47/c++/ostream:600:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = HashTable<Symbol>::HashEntry]'
Here is where I'm trying to print the vector in the HashTable class...
class HashTable
{
///...
void print()
{
typename vector<HashEntry>::iterator vIter = array.begin;
while(vIter != array.end())
{
cout<< *vIter << "\n";
++vIter;
}
}
private:
vector<HashEntry> array;
};
And my overloading in the Symbol class...
friend ostream & operator <<(ostream & outstream, Symbol & symbol) //overloaded to print out the the HashTable
{
int num = symbol.type;
string name = symbol.data;
outstream << name << " : " << num << "\n";
return outstream;
}
Not sure what you're doing there but you have defined a vector of HashEntry and calling cout on HashEntry but your operator<< overload operates on Symbol instead.
The operator<< for class Symbol should look something like this
class Symbol {
private:
friend ostream& operator<<(ostream &os, const Symbol &s);
int type;
string data;
};
ostream& operator<<(ostream &os, const Symbol &s)
{
os << s.data << " : " << s.type;
return os;
}

Conversion operators

This code is not compilable.
I can't find why in standard. Can someone explain?
#include <iostream>
#include <string>
template<typename T>
class S
{
public:
explicit S(const std::string& s_):s(s_)
{
}
std::ostream& print(std::ostream& os) const
{
os << s << std::endl;
return os;
}
private:
std::string s;
};
template<typename T>
std::ostream& operator << (std::ostream& os, const S<T>& obj)
{
return obj.print(os);
}
/*template<>
std::ostream& operator << <std::string> (std::ostream& os, const S<std::string>& obj)
{
return obj.print(os);
}*/
class Test
{
public:
explicit Test(const std::string& s_):s(s_)
{
}
//operator std::string() const { return s; }
operator S<std::string>() const { return S<std::string>(s); }
private:
std::string s;
};
int main()
{
Test t("Hello");
std::cout << t << std::endl;
}
Compiler output:
source.cpp: In function 'int main()':
source.cpp:47:17: error: no match for 'operator<<' in 'std::cout << t'
source.cpp:47:17: note: candidates are:
In file included from include/c++/4.7.1/iostream:40:0,
from source.cpp:1:
include/c++/4.7.1/ostream:106:7: note: 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>]
include/c++/4.7.1/ostream:106:7: note: no known conversion for argument 1 from 'Test' to 'std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&) {aka std::basic_ostream<char>& (*)(std::basic_ostream<char>&)}'
....
Thats because no conversions, except for array-to-pointer, function-to-pointer, lvalue-to-rvalue and top-level const/volatile removal (cf. c++11 or c++03, 14.8.2.1), are considered when matching a template function. Specifically, your user-defined conversion operator Test -> S<string> is not considered when deducing T for your operator<< overload, and that fails.
To make this universal overload work, you must do all the work at the receiving side:
template <class T>
typename enable_if<is_S<T>::value, ostream&>::type operator <<(ostream&, const T&);
That overload would take any T, if it weren't for the enable_if (it would be unfortunate, since we don't want it to interfere with other operator<< overloads). is_S would be a traits type that would tell you that T is in fact S<...>.
Plus, there's no way the compiler can guess (or at least it doesn't try) that you intended to convert Test to a S<string> and not S<void> or whatever (this conversion could be enabled by eg. a converting constructor in S). So you have to specify that
Test is (convertible to) an S too
the template parameter of S, when converting a Test, is string
template <class T>
struct is_S {
static const bool value = false;
};
template <class T>
struct is_S<S<T>> {
static const bool value = true;
typedef T T_type;
};
template <>
struct is_S<Test> {
static const bool value = true;
typedef string T_type;
};
You will have to convert the T to the correct S manually in the operator<< overload (eg. S<typename is_S<T>::T_type> s = t, or, if you want to avoid unnecessary copying, const S<typename is_S<T>::T_type> &s = t).
The first paragraph of #jpalecek's answer explains what the issue is. If you need a workaround, you could add a declaration like:
inline std::ostream& operator<< (std::ostream& os, const S<std::string>& s)
{ return operator<< <> (os, s); }
Since that overload is not a template, implicit conversions to S<std::string> will be considered.
But I can't see any way to do this for all types S<T>...