The question is published on by Tutorial Guruji team.
I want to define a function that accepts different types of containers: those that own the data (like std::vector
) and those that don’t (like boost::iterator_range
).
I wrote the following:
#include <vector> #include <boost/range/iterator_range.hpp> template<typename C> void fill(C& c, typename C::value_type v){ for (auto& j : c) j = v; } template<typename C> // How to avoid this implementation? void fill(const C& c, typename C::value_type v){ for (auto& j : c) j = v; } int main(){ std::vector<int> v(10); auto rng = boost::make_iterator_range(v.begin(), v.end()); fill(v, 1); // Case 1 -- Calling on std::vector fill(rng, 2); // Case 2 -- Calling on boost::iterator_range // Case 3 -- Calling with an r-value, requires the overload fill(boost::make_iterator_range(v.begin(), v.end()), 3); // Case 4 -- Should not be allowed // fill(std::vector<int>(10), 4); }
My first implementation of fill
works well, in that it accepts both kinds of iterators.
However, since boost::iterator_range
does not own the data, it can be const
(while the data is non-const) and so I would like to allow rvalues as arguments. This means that I need the second implementation.
Is this the only way to do this? Is there a way to avoid the second implementation?
Answer
You can use perfect forwarding:
template <typename C> void fill(C&& c, typename std::decay_t<C>::value_type v) { for (auto& j : c) j = v; }
This isn’t actually “perfectly forwarding” since you’re not looking to take advantage of move semantics, if you wanted to you should write
std::forward<C>(c)
instead ofc
¹
#include <boost/range/iterator_range.hpp> #include <vector> template <typename C> void fill(C&& c, typename std::decay_t<C>::value_type v) { for (auto& j : c) j = v; } int main() { std::vector<int> v(10); auto rng = boost::make_iterator_range(v.begin(), v.end()); fill(v, 1); // Case 1 -- Calling on std::vector fill(rng, 2); // Case 2 -- Calling on boost::iterator_range // Case 3 -- Calling with an l-value, requires the overload fill(boost::make_iterator_range(v.begin(), v.end()), 3); }
¹ Note that you have to be careful not to use-after-move if you do.