简体   繁体   中英

Overload operator for both std::vector and std::list

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:

  • it works only for containers defined with two template arguments (so ok for list and vectors; but not for sets or maps).
  • it might conflict with other template types using two arguments, for which there's no specialisation of the extractor.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM