Copy range up to delimiter
The third argument of copy_until could of course be a unary predicate, then instead of while (first != last &amp;&amp; *first != delim) it would say while (first != last &amp;&amp; !p(*first)).


Even better: remove the I last argument entirely! Just use the predicate for the whole thing, and the condition becomes while (!p(first)).



You're returning I (the place you stopped parsing the input stream), but similar functions in the STL return O (the place they stopped outputting to the output stream). The latter is more useful in STL cases, because typically the caller knows where he stopped parsing the input: it's last!

In your case, both I and O are useful; so why not return both?

template&lt;typename I, typename O, typename Pred&gt;
std::pair&lt;I, O&gt; copy_until(I first, O d_first, Pred pred)
{
    while (!pred(first)) {
        *d_first = *first;
        ++first;
        ++d_first;
    }
    return {first, d_first};
}


Your main thus becomes:

int main()
{
    std::istreambuf_iterator&lt;char&gt; first(std::cin);
    std::istreambuf_iterator&lt;char&gt; last;
    std::ostreambuf_iterator&lt;char&gt; d_first(std::cout);

    while (first != last) {
        std::tie(first, std::ignore) = copy_until(
            first, d_first, [=](auto it){ return it == last || *it == '\n'; }
        );
        if (first == last) break;  // Whoops!
        std::cout.put(*first);
        ++first;
    }
}


The // Whoops! comment indicates the fix for a crashing bug in your original main.



Boost.Algorithm actually has both copy_until and its inverse, copy_while: http://marshall.calepin.co/copy_while.html
Strangely, Boost's version takes last as a parameter exactly the way yours does. In Boost's case, I think that might be because they're trying to target C++03, which doesn't have lambdas, so there's no way that's both easy and lightweight to express [last](auto it) { return it != last &amp;&amp; *it != delim; } — in C++03 you had to do that with boost::bind and maybe boost::function as well.




  I am not sure how to declare that the two iterators need to point to the same type as the type of the delimiter or the argument to the unary predicate.


The short answer is "don't"; complicated dependencies like that usually indicate that you've put the abstraction boundary in the wrong place (e.g. in this example, passing First, Last, Delim instead of First, Pred). But just for the record, you could do something crazy with enable_if, which can go on the return type of the function or on one of its parameters or on one of its template parameters, your choice.

auto copy_until(I first, I last, O d_first, T delim)
    -&gt; std::enable_if_t&lt;std::is_same_v&lt;decltype(*first), T&gt;, I&gt;
{
    // ...
}


I'm pretty sure you don't want to assert that decltype(*d_first) is T, because that would prevent you from using something like a back_insert_iterator&lt;vector&lt;T&gt;&gt; with this algorithm.