Search code examples
c++templatesconstantstemplate-argument-deduction

How to deduce const and non-const type without duplicating functions?


Let's say I have this function: (which pretty much runs a function on each value in container, and then returns a vector of the result of each iteration)

#include <vector>
using std::vector;

template<class F, class V>
auto vmap(const F &f, const V &v) -> vector<decltype(f(v[0]))> {
    vector<decltype(f(v[0]))> result;
    result.reserve(v.size());
    for (auto &p : v)
        result.push_back(f(p));
    return result;
}

I can't call it like this:

vector<int> vint = {1,2,3,4,5};
vmap([](auto &p) {return p++;},vint);

Because the parametered vector is const, to make it happen I will have to create two vmap that get non-const V and one that is const.

It starts to feel too much when there are multiple containers/vectors passed to a function, because it makes me write 2^containers_count functions.

Is there any(-dirty but working) solution for this?


Solution

  • You could use an forwarding reference to bind to both normal l-value references (e.g. std::vector<int>&) and r-value references (std::vector<int>&&).

    The downside is that you're never able to pass by value (only ref, const ref, or r-value ref), though I don't think that'll be a problem for you:

    template<class F, class V>
    auto vmap(F&& f, V&& v) {
        vector<decltype(f(v[0]))> result;
        result.reserve(v.size());
        for (auto& p : v)
            result.push_back(f(p));
        return result;
    }
    

    Demo

    Note that the lambda you pass must be applicable to both const if you're going to pass a const container (Thanks, Miles), so p++ is out of the question (Although beware that one template instantiation modifies the input and the other doesn't, which may be unexpected):

    vector<int> vint = {1,2,3,4,5};
    vmap([](auto &p) {return p++;},vint);
    const std::vector<int> vint2 = vint;
    vmap([](auto &p) {return p+1;},vint2);