I want to overload operator<<
for both std::list
and std::vector
with the following code. But the two functions are almost the same. Is there any way to combine them, ie , create a more generic overload?
#include <iterator>
#include <iostream>
#include <vector>
#include <list>
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &v)
{
if (!v.empty())
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
return out;
}
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::list<T> &v)
{
if (!v.empty())
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
return out;
}
int main()
{
std::cout << std::vector<int>({1, 2, 3, 4}) << std::endl;
std::cout << std::list<int>({1, 2, 3, 4}) << std::endl;
return 0;
}
You can use template with template arguments like in the following example:
template <typename T, typename A, template <typename X, typename Y> class C>
std::ostream &operator<<(std::ostream &os, const C<T,A> &container)
{
if(!container.empty())
std::copy(container.begin(), container.end(), std::ostream_iterator<T>(os, " "));
return os;
}
int main() {
list<int> l{1,2,3,4,5};
vector<string> v{"one","two","three"};
cout<<l<<endl<<v;
return 0;
}
Online demo .
Bu the way, you may find other example for working with templates of templates in this SO question.
But you have to be careful with this kind of construct:
Remark: If you look for a general purpose solution, you should better think of creating an adapter template that uses an iterator as parameter, and then write the general purpose extractor for this adaptor.
Enter C++20 and the likely inclusion of Concepts and Ranges , the solution to your problem is something that could be simplified greatly.
A Concept is basically a template parameter with constraints, eg
// Taken from https://en.cppreference.com/w/cpp/experimental/constraints
template <typename T>
concept bool Integral = std::is_integral<T>::value;
template <Integral T> // Using concept Integral.
void foo(T i) { /* ... */ } // 'i' has to be integral, or compile time error.
Now, the concept of a Range (simplified) is something that complies to the interface (pseudocode):
Range {
begin()
end()
}
Using this, one could write something like the following:
template <Range T>
std::ostream& operator<<(std::ostream& out, T&& rng) {
std::copy(std::forward<T>(rng), std::make_ostream_joiner(out, ", "));
return out;
}
And it would just work for anything that has a begin()
and end()
, eg
std::cout << std::vector{1, 2, 3, 4, 5} << std::endl; // Ok
std::cout << std::list{1, 2, 3, 4, 5} << std::endl; // Ok
Additionally, note that I used std::make_ostream_joiner
instead of std::ostream_iterator
. It makes use of a new C++20 iterator std::ostream_joiner
that writes the delimiter between every two objects skipping the extra trailing delimiter, ie you would get "1, 2, 3, 4, 5"
instead of "1, 2, 3, 4, 5, "
.
Let's hope all these features make it into C++20 :)
Note: All examples given are hypothetical C++20 code and does not currently compile using any release-build compiler that I know of.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.