Im trying to do an override on a simple struct like this:
struct Node {
int data1;
int data2;
ostream& operator<<(ostream &o, const Node &n)
{
o << "(a: " << data1 << ", b: " << data2 << ")";
return o;
}
};
Im getting: error C2804: 'operator <<' too many parameters
So, if i remove the second parameter:
ostream& operator<<(ostream &o)
Then i get: Error: binary '<<' : no operator found which takes a right-hand operand of type 'const Node'
What is coming on here?
operator<< needs two operands. There are two ways to define an operator<< overload function between two types.
As a member function. That's how operator<< overloads between std::basic_ostream and some of the basic types are defined. When you use std:cout << 10;, it gets resolved to the overload operator std::basic_ostream<char>::operator<<(int)
When you define it as member function, the LHS of the operator is an instance of the class. The RHS of the operator is the argument to the function. That's why when you define it as member function, it can only have one argument.
As a free function. That's how operator<< overloads between std::basic_ostream and custom types are defined. These functions must have two arguments. The first argument is the LHS of the operator and the second argument is the RHS of the operator.
For this reasons, you have to define operator<< overload between std::ostream and your class as a free function, with std::ostream& as the first argument type and Node const& as the second argument type.
std::ostream& operator<<(std:ostream& os, Node const& node);
At times like these, I wish the standard library had implemented a function:
template <typename T>
std::ostream& operator<<(std::ostream& os, T const& t)
{
return t.serialize(os);
}
Then, any class could provide the function
std::ostream& serialize(std::ostream& os) const;
and be ready to be used like all the basic types with std::ostream.
std::ostream& operator<<(std::ostream&, ...) needs to be a free function.
Move it outside the class and it will work.
The reason it is so is because defining operator<<(std::ostream&) (or other binary operators) inside the class implies that object is the LHS operand. You would have to write smth crazy like:
Node << std::cout;
Related
I have this code
template <typename T>
class KeyValueProperty {
protected:
T value = T();
std::string key = "";
public:
KeyValueProperty(const std::string & key) : key(key) { }
T & operator = (const T &i) { return value = i; };
operator const T & (){ return value; };
};
struct T2 {
KeyValueProperty<std::string> x {"x"};
KeyValueProperty<double> y {"y"};
};
and in main
T2 tx;
tx.x = "hellow";
tx.y = 10;
std::cout << static_cast<std::string>(tx.x) << ::std::endl;
std::cout << tx.y << ::std::endl;
This is working correctly. However, doing only this
std::cout << tx.x << ::std::endl;
will end up in
error C2679: binary '<<': no operator found which takes a right-hand operand of type 'Test::KeyValueProperty' (or
there is no acceptable conversion)
Is it possible to have auto-conversion, or I must manually call casting?
The reason t.y works even without a custom operator<< is because there already exists an operator<<(std::ostream&, double), and the compiler can also see that it can make a double out of your class. It does so and we're happy.
However, there is no operator<<(std::ostream&, std::string). If there was, the same logic would apply and we'd still be happy. Instead, there is:
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);
That is, a generic insertion operator for any kind of basic_string.
Although there exist some template arguments that would make this as if it were operator<<(std::ostream&, std::string), the compiler isn't going to try and guess which possible template arguments would allow it to subsequently match a conversion of your class to the result. There are too many combinations, so this isn't permitted.
This is why you had to explicitly turn your object into a std::string (aka std::basic_string<char>) - this removes one layer from the problem and it can do regular old type deduction to make this work.
The right solution is to give your wrapper class an insertion operator, to sidestep this issue.
You must provide an appropriate overload of operator<<, for example:
template<class T>
std::ostream& operator<<(std::ostream& os, KeyValueProperty<T> const& kvp)
{
return os << T(kvp);
}
I'm working through an assignment in which I must overload the insertion operator to take a Node object. I've created the operator overload function outside the class definition, but inside the node.h file. Everything compiles fine, but the overloaded operator is not called, instead I get simple the address of the object.
I'm prohibited from modifying the calling code, so any changes must be to the operator overload.
My code as it stands right now:
/** OPERATOR << ***********************************/
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> *& pHead)
{
out << "INCOMPLETE";
return out;
}
Right now, I just want to ensure the overloaded operator is called. I'll fix the output code once I know I'm calling the right operator.
The calling code:
// create
Node <char> * n = NULL;
// code modifying n
// display
cout << "\t{ " << n << " }\n";
Note that the parameter pHead's type is a reference to non-const, const Node<T>* is a non-const pointer to const, the argument n's type is Node<T>* (i.e. a non-const pointer to non-const). Their type doesn't match, Node<T>* need to be converted to const Node<T>*, which is a temporary and can't be bound to reference to non-const.
In short, you can't bind a reference to non-const to an object with different type.
But reference to const could be bound to temporary, so you can change the parameter type to reference to const:
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> * const & pHead)
// ~~~~~
Or change it to passed-by-value, Node<T>* will be implicitly converted to const Node<T>* when passing as argument. (Passing pointer by reference to const doesn't make much sense.)
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> * pHead)
At last, overloading operator<< with pointer type looks weird. The most common form with user-defined type would be:
template<class T>
std::ostream & operator << (std::ostream & out, const Node <T> & pHead)
The problem is that the inserter takes a parameter of type const Node<T>*, but it's called with an argument of type Node<T>*; there is no conversion from T* to const T*. So the "fix" is to remove the const from the stream inserter.
But, as hinted at in a comment, having an inserter that takes a pointer to a type is a bad idea. It should take a const Node<T>&, like all the other inserters in the world. I gather that this is a constraint imposed by an assignment; if so, it's idiotic. You're being taught badly.
I have some Logging::Logger class with the following functions:
template<typename T>
const Logger& Logger::operator<<(const T& in) const {
// ...
return *this;
}
const Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&)) {
// ...
return *this;
}
And the following code:
loggerInstance << "ID: " << 5 << endl;
And I'm getting the following error though all operators seems to be implemented:
error C2678: binary '<<': no operator found which takes a left-hand
operand of type 'const Logging::Logger' (or there is no acceptable
conversion)
Of course, without endl everything is working.
I've looked at the following answer:
std::endl is of unknown type when overloading operator<<
What am I missing?
Because your overloaded operators return a const Logger &, it follows that they must be const class methods, in order for you to be able to chain them together:
const Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&)) const
However, it's better if they were not const class members, and returned a Logger &, instead:
template<typename T> Logger& Logger::operator<<(const T& in)
Logger& Logger::operator<<(std::ostream& (*os)(std::ostream&))
This would be because, presumably, operator<< would be modifying the Logger instance, in some way. If not, you can use const objects and methods, here.
As in the question, if I define a string operator in my class:
class Literal {
operator string const () {
return toStr ();
};
string toStr () const;
};
and then I use it:
Literal l1 ("fa-2bd2bc3e0");
cout << (string)l1 << " Declared" << endl;
with an explicit cast everything goes right, but if I remove the (string) the compiler says that it needs a cast operator declared in std::string. Shouldn't it cast my type automatically?
SOLVED: I'm overloading operator<< (ostream& os, const Literal& l).
No.. std::string would have to have a constructor that took Literal as an argument.
What you could do is overload operator << for your Literal class and have it cast and insert into the stream in there.
ostream &operator <<(std::ostream &stream, const Literal &rhs)
{
stream << (string) rhs;
return stream;
}
Short answer: Keep using a cast or toStr(), or write your own operator<< function. (I would prefer l1.toStr() to (string)l1.)
Long answer:
This might work if the Standard Library had a function
std::ostream& operator<<( std::ostream&, std::string const& );
Which it almost does, but not technically. Both ostream and string are really typedefs of template instantiations. And there's a template function for inserting one into the other.
// This is somewhat simplified. For the real definitions, see the Standard
// and/or your complying implementation's headers.
namespace std {
typedef basic_string<char> string;
typedef basic_ostream<char> ostream;
template <typename CharT>
basic_ostream<CharT>& operator<<(
basic_ostream<CharT>&,
basic_string<CharT> const&);
}
So when you use cout << str where the type of str is std::string, it can figure out to use that template function, with CharT = char.
But C++ doesn't allow you to have the compiler figure out both an implicit type conversion (Literal to string) and deduce template function template parameters (CharT = char) on the same call.
how can i overload "<<" operator (for cout) so i could do "cout" to a class k
The canonical implementation of the output operator for any type T is this:
std::ostream& operator<<(std::ostream& os, const T& obj)
{
os << obj.get_data1() << get_data2();
return os;
}
Note that output stream operators commonly are not member functions. (That's because for binary operators to be member functions they have to be members of their left-hand argument's type. That's a stream, however, and not your own type. There is the exception of a few overloads of operator<<() for some built-ins, which are members of the output stream class.)
Therefor, if not all data of T is publicly accessible, this operator has to be a friend of T
class T {
friend std::ostream& operator<<(std::ostream&, const T&);
// ...
};
or the operator calls a public function which does the streaming:
class T {
public:
void write_to_stream(std::ostream&);
// ...
};
std::ostream& operator<<(std::ostream& os, const T& obj)
{
obj.write_to_stream(os);
return os;
}
The advantage of the latter is that the write_to_stream() member function can be virtual (and pure), allowing polymorphic classes to be streamed.
If you want to be fancy and support all kinds of streams, you'd have to templatize that:
template< typename TCh, typename TTr >
std::basic_ostream<TCh,TTr>& operator<<(std::basic_ostream<TCh,TTr>& os, const T& obj)
{
os << obj.get_data1() << get_data2();
return os;
}
(Templates, however, don't work with virtual functions.)