简体   繁体   中英

Providing a correct std::copy_if predicate

The aim of the example program is to copy every third item from source to target with std::copy_if . Based in the reference , the copy should happen whenever the predicate returns with true, but this is not the case with the below code.

#include <iostream>
#include <vector>
#include <algorithm>

using std::vector;

int main(int argc, char** agrs){
    vector<double> source(15, 0.5);
    vector<double> target(15, 1.1);
    int index = 0;
    std::copy_if(
        source.begin(),source.end(),target.begin(),
        [&](double number){
            index = ((index + 1) % 3);
            std::cout << "["<< index << "]->" << (0 == index) << "\t";
            return (0 == index);
        }
    );
    std::cout << std::endl;

    std::for_each(source.begin(),source.end(),[](double value){
            std::cout << "["<< value << "]\t";
    });
    std::cout << std::endl;
    std::for_each(target.begin(),target.end(),[](double value){
            std::cout << "["<< value << "]\t";
    });
    std::cout << std::endl;
    return 0;
}

The output is the following:

[1]->0  [2]->0  [0]->1  [1]->0  [2]->0  [0]->1  [1]->0  [2]->0  [0]->1  [1]->0  [2]->0  [0]->1  [1]->0  [2]->0  [0]->1
[0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [0.5]
[0.5]   [0.5]   [0.5]   [0.5]   [0.5]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]   [1.1]

The first row is showing the actual copying as well as the return value of the output predicate. The second row is the source vector, and the third is the target vector.

It would be fair to expect every 3rd element to be copied, based on the predicate, however that is not the case?

Why is that? How can the logic be fixed to fulfill its intended purpose?

Unfortunately, sometimes cplusplus has wrong/misleading information. They write:

result

Output iterator to the initial position of the range where the resulting sequence is stored. The range includes as many elements as [first,last).

And that is wrong. The output range has as many elements as the predicate returns true . Others are not copied and the target iterator is not incremented in that case. copy_if works just like expected in your example.

I suggest this reference: https://en.cppreference.com/w/cpp/algorithm/copy

It also does not mention explicitly that the output iterator is only advanced when actually an element was copied. But it also does not state otherwise. Looking at the possible implementation should make things more clear:

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

You can see that d_first is only incremented when pred(*first) returns true .

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM