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.