How to Write the Range-based For-Loop With Argv?
Usually, the first thing I do with argc
and argv
is this:
std::vector<std::string> arguments(argv, argv + argc);
Now I have a vector of strings to work with and I can easily use not only the range-based for loops, but also C++ standard library facilities.
for(std::string& s : arguments) {
// do stuff...
}
The wikipedia code works because the type of my_array
is a variable of array type.
The original code does not work, because argv
is not an array. The syntax char* argv[]
may make it look like it is an array, but that's just a sad artifact of C syntax. char* argv[]
is exactly the same as char** argv
. argv
is not an array; it's actually just a pointer.
The range-based for loop works on:
- arrays;
- any type that has member functions
begin()
andend()
that return iterators; - any type for which exist non-member functions
begin
andend
that can be called likebegin(x)
andend(x)
, withx
being the thing that you're iterating over.
As you can see, pointers are not part of this list.
You don't, because the system can't tell how long argv
is at compile time. Someone can likely find the proper section of the standard to quote you about this.
There is a way around it though, and that's to create a custom class to wrap argv
. It's not even that hard.
class argv_range {
public:
argv_range(int argc, const char * const argv[])
: argc_(argc), argv_(argv)
{
}
const char * const *begin() const { return argv_; }
const char * const *end() const { return argv_ + argc_; }
private:
const int argc_;
const char * const *argv_;
};
Here's how you use it:
for (const char *arg: argv_range(argc, argv)) {
// Do something.
}
Yeah, I use a lot of const
s. Basically, argv
is an array of character pointers, none of which should be modified, each pointing to a string, none of the characters of which should be modified either.
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!