简体   繁体   中英

Template Member Function to Write to Output Iterator

I thought I would be smart and create member functions that accepted output iterators. This way, I could avoid returning a collection or taking a collection by reference. For example:

template <typename TOutIterator>
void getHeaderNames(TOutIterator destination);

template <typename TOutIterator>
void getHeaderValues(std::string const& name, TOutIterator destination);

These functions will write their results to whatever iterator is passed in. This way, I don't have to worry whether I'm writing to a set, vector or ostream.

Now I don't feel so smart. I want to make these functions virtual so I can stub out the implementation in test. Unfortunately, template member functions can't be virtual, which makes sense.

Is there a way to keep these functions generic (write to anything) and allow them to be virtual at the same time? I want to avoid writing everything to a vector only to turn around and write it to standard out or whatever.

Let me know if I need to explain my situation more clearly.

You could use type erasure to manipulate polymorphic iterators, like the any_iterator proposed by Thomas Becker (and later implemented in Boost.Range ). You would end up with something along those lines:

typedef any_iterator<
  std::string, // Value
  Writable,    // Access
  Forward,     // Traversal
> StringOutputIterator; // can represent any output iterator accepting strings

virtual void getHeaders(StringOutputIterator destination);

The idea of type erasure is to have a common base class for a set of otherwise unrelated types (which happen very often in C++, due to the use of templates). For instance, std::function applies this idiom to callable objects, by allowing the manipulation of function pointers, functors or lambdas in a similar way.

I faced a similar problem, and I didn't want to add Boost to my project... so I decided not to use iterators at all.

I ended up using a std::function instead:

void getHeaderNames(std::function<void(std::string)> destination);

void getHeaderValues(std::string const& name, std::function<void(std::string)> destination);

Then, instead of std::back_inserter , I provide a lambda that executes push_back :

std::vector<std::string> v;
getHeaderNames([&](auto name) { v.push_back(std::move(name)); });

One way to keep them generic, and I usually see this in practice, is two overload a stream output operator (if it makes sense) or take an std::ostream& .

Of course it depends on your exact situation: Are you writing an algorithm for which iterators make more sense? Or just want to dump an object's content?

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