Search code examples
c++11

Why does copy_if implementation not take unary_predicate as const reference parameter


template<class InputIt, class OutputIt, class UnaryPred>
OutputIt copy_if(InputIt first, InputIt last,
                 OutputIt d_first, UnaryPred pred)
{
    for (; first != last; ++first)
        if (pred(*first))
        {
            *d_first = *first;
            ++d_first;
        }
 
    return d_first;
}

Above is the sample implementation from cppreference for copy_if. My question is why the 3rd parameter is not a const reference?


Solution

  • Let's see what would we get if pred was a const&

    std::copy_if doesn't take a const& predicate so lets implement our copy_if_const_ref

    #include <vector>
    #include <iostream>
    
    /* Custom copy_if implementation using const& */
    template <typename InputIt, typename OutputIt, typename UnaryPred>
    OutputIt copy_if_const_ref(InputIt first, InputIt last, OutputIt d_first, const UnaryPred& pred) {
        for (; first != last; ++first) {
            if (pred(*first)) {  /* error: no match for call to '(const main()::<lambda(int)>) (int&)' */
                *d_first++ = *first;
            }
        }
        return d_first;
    }
    
    int main() {
        std::vector<int> n1 = {1, 2, 3, 4, 5, 6};
        std::vector<int> n2;
    
        int count = 0;
    
        /* Mutable lambda that modifies a captured state because i simply want to do this */
        auto pred = [=](int x) mutable {
            ++count;
            return x % 2 == 0;
        };
    
        /* This will fail to compile because the lambda cannot modify its state */
        copy_if_const_ref(n1.begin(), n1.end(), std::back_inserter(n2), pred);
    
        return 0;
    }
    

    This code won't compile because the predicate is passed as a const UnaryPred&, which prevents the mutable lambda from modifying its captured state

    A fix to this code is as simple as modifying the predicate to be

    auto pred = [/* = */](int x) /* mutable */ {
        /* ++count; */
        return x % 2 == 0;
    };
    

    So if std::copy_if was behaving just like copy_if_const_ref, I would have to edit my predicate to use it. And if I have to maintain a state and update it, I wouldn't be able to use std::copy_if and I would have to implement my own.

    The list of the reasons for the predicate not to be a const& goes on, that was just a simple use case where my copy_if_const_ref fails.

    Why would the "Standards" put limits to the flexibility of the "Standard functions"?