Suppose that you have the following code:
#include <iostream>
template <typename T>
class Example
{
public:
Example() = default;
Example(const T &_first_ele, const T &_second_ele) : first_(_first_ele), second_(_second_ele) { }
friend std::ostream &operator<<(std::ostream &os, const Example &a)
{
return (os << a.first_ << " " << a.second_);
}
private:
T first_;
T second_;
};
int main()
{
Example example_(3.45, 24.6); // Example<double> till C++14
std::cout << example_ << "\n";
}
Is this the only way to overload the operator<<
?
friend std::ostream &operator<<(std::ostream &os, const Example &a)
{
return (os << a.first_ << " " << a.second_);
}
In terms of performance, is it the best way to overload it or are there better options to do this implementation?
I believe that the comments have answered your question well enough. From a pure performance standpoint, there likely is no "better" way to overload the <<
operator for output streams because your function is likely not the bottleneck in the first place.
I will suggest that there is a "better" way to write the function itself that handles some corner cases.
Your <<
overload, as it exists now, will 'break' when trying to perform certain output formatting operations.
std::cout << std::setw(15) << std::left << example_ << "Fin\n";
This does not left align your entire Example
output. Instead it only left aligns the first_
member. This is because you put your items in the stream one at a time. std::left
will grab the next item to left align, which is only a part of your class output.
The easiest way is to build a string and then dump that string into your output stream. Something like this:
friend std::ostream &operator<<(std::ostream &os, const Example &a)
{
std::string tmp = std::to_string(a.first_) + " " + std::to_string(a.second_);
return (os << tmp);
}
It's worth noting a few things here. The first is that in this specific example, you will get trailing 0's because you don't get any control over how std::to_string()
formats its values. This may mean writing type-specific conversion functions to do any trimming for you. You may also be able to use std::string_views
(to gain back some efficiency (again, it likely doesn't matter as the function itself is probably still not your bottleneck)), but I have no experience with them.
By putting all of the object's information into the stream at once, that left-align will now align the full output of your object.
There is also the argument about friend vs. non-friend. If the necessary getters exist, I would argue that non-friend is the way to go. Friends are useful, but also break encapsulation since they are non-member functions with special access. This gets way into opinion territory, but I don't write simple getters unless I feel that they are necessary, and I don't count <<
overloads as necessary.