Defining a function for different containers Code Answer

Hello Developer, Hope you guys are doing great. Today at Tutorial Guruji Official website, we are sharing the answer of Defining a function for different containers without wasting too much if your time.

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 of c¹

Live On Coliru

#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.

We are here to answer your question about Defining a function for different containers - If you find the proper solution, please don't forgot to share this with your team members.

Related Posts

Tutorial Guruji