The vector solution proposed copies the array (the pointers only, not the strings1 - but still). Unnessary. The argv_range solution is what I would have tried, too, if I absolutely wanted to enforce a range based loop. But that produces a lot of extra code (admitted, only once, if you write it to a header file and keep it, but still).
The classic loop appears so easy to me that I allow myself just to post it, I don't consider it worth to have all this effort just to have a range based loop...
for (char** a = argv; *a; ++a)
{
    // use *a, or if you don't like:
    char* arg = *a;
    // use arg...
}
Or, if you won't ever need the argv array afterwards any more:
for (++argv; *argv; ++argv)
{
    // use *argv, or if you don't like:
    char* a = *argv;
    // use a...
}
There is a little difference, you might have noticed: In the first variant, I iterate over all the values, in the second, I leave out the first one (which normally is the program name we are not interested in in many cases). The other way round, for each:
for (char** a = argv + 1; *a; ++a);
for (; *argv; ++argv);
Note that all these variants profit from the fact that the strings array itself is null-pointer-terminated as well, just as any of the strings is null-character-terminated, thus the simple checks for *a or *argv.
1This applies only, if using std::vector<char*>; if using std::vector<std::string>, as actually proposed, even the strings themselves are copied!