Implementation of Linux cp in modern C++
I see some things that may help you improve your code.

Fix the bug

The program loops infinitely (actually, as @CrisLuengo correctly points out in a comment, it's not actually infinite.  However, it loops \$2^{54}\$ times for each 1024 bytes in the input file on a 64-bit machine, so it will certainly feel infinite for any human being waiting for the copy to complete!) if the output file already exists or can't be created.  That's a bug that should be fixed.  Generally, when a call can fail (as with open) it is important to test the result for failure before acting on the result.

Use std::fstream for portability

This could be a completely portable program using only standard C++ libraries.  To do that, you'd omit &lt;fcntl.h&gt; and &lt;unistd.h&gt; and just use fstream.  I'm guessing you may have chosen to use the low-level functions for speed, but it's important to measure rather than assume.  The results may surprise you!

Delete file before writing for speed

On several Linux platforms, overwriting an existing program is slower than creating one.  For that reason, one way to make sure that the program is as fast as possible is to call std::remove before doing the copy.

Return valid error codes

A value of 0 as an error indicates no error on Linux and many other platforms.  If you execute cp with no arguments, it returns with a value of 1.  If your program is intended to be an exact replacement, I'd recommend returning 1 instead of 0 if there are missing arguments.

Check for arguments using argc

We can determine if there are the correct number of arguments by examining argc.  The way you're doing it has a problem:

if ( argv[1] == nullptr || argv[2] == nullptr ){


The problem is that if there are zero arguments, argv[1] and argv[2] might not be pointing to anything, so dereferencing them is undefined behavior and therefore not good programming practice.  In particular, you should be aware that argc might be zero which would make testing argv[1] undefined behavior.

Don't use std::string if what you need is a char *

In this case, both of the command line arguments are turned into std::strings but then used exclusively with c_str() to turn them back int char *.  Better would be to simply use char * without conversions to and from std::string.

Prefer std::array to std::vector

If the bounds of your std::vector are known at compile-time (as in this case), then it's generally better to use std::array rather than std::vector.  The std::vector has provisions for resizing that this program doesn't use and doesn't need.  In fact, for this program, there's actually not much difference between a plain C-style array and a std::array.

Name your constant

The buffer size is 1024 bytes, which is probably a bit on the small size.  I think it would be a slight improvement to increase the size and also to name that constant.

Think about non-file files

Under Linux, we have not just files but also devices and named pipes.  Think about whether your program does what you intend if the input is a named pipe.  What happens if we try to copy to /dev/null ?

An alternative version

Using most of those suggestions, here's an alternative:

#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;cstdio&gt;

int main(int argc, char *argv[]) {
    if (argc != 3) {
        std::cout &lt;&lt; "Usage: moderncp srcfile destfile\n";
        return 1;
    }
    std::ifstream in{argv[1]};
    std::remove(argv[2]);
    std::ofstream out{argv[2]};
    if (!out) {
        std::cerr &lt;&lt; "Could not create output file " &lt;&lt; argv[2] &lt;&lt; '\n';
        return 1;
    }
    static constexpr std::size_t buffsize{1024};
    char buffer[buffsize];
    while (in.read(buffer, buffsize)) {
        out.write(buffer, buffsize);
    }
    out.write(buffer, in.gcount());
}


Note that because we're using fstreams for input and output, we don't have to explicitly close the files because the destructor will do so automatically as well as flushing the output buffer.

Results

On my 64-bit Linux machine, copying a 5.3GiB file took 1:29 with the original program, 1:25 with this rewritten version and 1:21 with cp.  Changing the rewritten version to use an 8K buffer allowed it to complete in 0:59, so that's clearly an improvement.