2

I've been reading "Effective STL" by Scott Meyers and I've noticed quite a few examples in the book where he prints the contents of a container by using std::copy to copy the elements to std::cout. To me this just feels very difficult to read compared to the alternative of using a range based for loop (perhaps I just need to get used to it?)

// Given the following
std::vector<int> values = { 1, 2, 3, 4, 5, 6, 7, 8 };

// Print using `std::copy`
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(std::cout, ", "));

// Print using a loop
for (const auto value : values)
{
    std::cout << value << ", ";
}

This post seems to suggest that there is no significant performance gain for using std::copy:

is std::copy faster than std::cout for output?

Do some people really find the std::copy approach more readable than the range based for loop?


Maybe if I only want to print half of the values...

std::copy(values.begin(), values.begin() + values.size()/2, std::ostream_iterator<int>(std::cout, ", "));

...but even then I can just do this:

for (auto it = values.begin(); it != values.begin() + values.size()/2; ++it)
{
    std::cout << *it << ", ";
}

So my question is why would anyone prefer the std::copy approach? Are there any situations where you can prove that std::copy is truly a better option (or the only option)?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • 1
    I think this boils down to being purely opinion based. In general standard algorithms arent magic and you can always replace them with a handwritten loop or vice versa, so it is mainly about style and preferences – 463035818_is_not_an_ai Jan 27 '20 at 20:29
  • 1
    Note that your last example is problematic with non-random access iterators. For example `std::list`. You can't do `iter + n`, though you can `std::next(iter, n)` at the cost of linear complexity. – François Andrieux Jan 27 '20 at 20:30
  • There is advice out there that says: "No raw loops". If you agree with that, then `std::copy` is solution. Personally, I don't mind trivial raw loops so I would use the range for for the full range, or `copy` for any slice of the range. – NathanOliver Jan 27 '20 at 20:32
  • That said once we get `std::span`, I'll probably use a `span` and a ranged for. – NathanOliver Jan 27 '20 at 20:34
  • @NathanOliver, I've heard that advice before but then I was trying to implement the Fast Fourier Transform in C++ and found lots of situations where I couldn't quite figure out how to avoid raw loops haha. Maybe i'm just not as experienced with STL as I need to be yet but "no raw loops" seems like a bit if an unrealistic blanket statement. – tjwrona1992 Jan 27 '20 at 20:35
  • 2
    Check when that book was published. It may be completely ignorant of C++11, which include `auto` variables (at least those with the current meaning) and range-based for loops. – Ulrich Eckhardt Jan 27 '20 at 20:36
  • @UlrichEckhardt yes that is true, but an iterator based loop could have still been done for the first example instead of a range based `for` so the question still remains the same. – tjwrona1992 Jan 27 '20 at 20:37
  • @tjwrona1992 I don't think the advice applies to cases like that. I think it's more for "user code". i.e., the driver code. – NathanOliver Jan 27 '20 at 20:40
  • You can always use a loop instead of `copy()`. Sometimes, `copy()` is more expressive because it has a higher abstraction level. This is a matter of taste though. Also, consider the loop with C++98, where you had to declare the full iterator type. There, readability of `copy()` finishes better in comparison. – Ulrich Eckhardt Jan 27 '20 at 20:40
  • True, without `auto` I guess I see a stronger argument for the readability of `copy` – tjwrona1992 Jan 27 '20 at 20:41
  • @UlrichEckhardt has nailed part of it. Effective STL was written in 2001. Language has changed a bit since then, more than a little because of Meyer's books. – user4581301 Jan 27 '20 at 20:43
  • @user4581301, yeah I've noticed quite a bit of what he says about passing function objects to STL algorithms has become obsolete now that the language includes lambda functions. – tjwrona1992 Jan 27 '20 at 20:50

1 Answers1

4

You can look at the possible implementation at cppreference:

template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last, 
              OutputIt d_first)
{
    while (first != last) {
        *d_first++ = *first++;
    }
    return d_first;
}

There is nothing magic about it. It really is just a loop that copies elements from Input to Output. Of course it does not use a range-based for loop (it has iterators not the container). It uses a while instead of for, which is just a cosmetic difference.

Are there any situations where you can prove that std::copy is truly a better option (or the only option)?

No. You can always manually write code that does the same as the algorithm. Consider that standard algorithms are not supposed to be better than handwritten code. They are supposed to be as good as handwritten code and not worse. The real benefit is that using an algorithm leaves less room for mistakes and it can be recognized more easily. There are many different ways to write a loop that copies something, but there is only one std::copy, this actually helps to read code.

So it all boils down to opinions...

Do some people really find the std::copy approach more readable than the range based for loop?

Yes, some people believe that one should minimize the use of handwritten loops in favour of algorithms. However, luckily there can be more than one opinion and if "no raw loops" would be what the standard tries to enforce, then probably range-based for loops would have never been included.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185