22

From the c++0x Wikipedia site:

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
    x *= 2;
}

So why does this code not work?

int main(int argc, char* argv[])
{
    for (char *arg : argv)
    {
        // Do something.
    }
}

Error:

main.cpp:36: error: no matching function for call to ‘begin(char**&)’

I am using Qt with g++ 4.6.1 on Ubuntu 11.10.

Additional Information

Is There a Range Class in C++0x

Range-Based For-Loop Statement Definition Redundance

Community
  • 1
  • 1
IAE
  • 2,213
  • 13
  • 37
  • 71
  • Note that (depending, of course, on the nature of the possible arguments and the type of processing you're trying to do on them) using a range-based for loop in this specific situation may be quite limiting - because you're forced to process one argument per loop iteration. Using an explicit iterator (or pointer) lets you advance immediately from one argument to the next - often the desired action when processing arguments like "-f myfile.out". With a range-based for loop you're likely to end up needing explicit state variables or similar. – Jeremy Apr 05 '17 at 07:53

6 Answers6

31

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() and end() that return iterators;
  • any type for which exist non-member functions begin and end that can be called like begin(x) and end(x), with x being the thing that you're iterating over.

As you can see, pointers are not part of this list.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • 1
    +1: That works too. Though it's not as efficient, it has its distinct advantages. – Omnifarious Dec 20 '11 at 09:27
  • @Fernandes: This is my usual procedure as well, but for this program I already know what type of input to expect, so I tried to avoid parsing it into a vector and then a second vector of the expected type. I just wanted to process each incoming argument and then convert it to the right type before insertion into my vector. – IAE Dec 20 '11 at 09:29
  • @SoulBeaver well, then your only option is to create a range type, like Omnifarious showed :) – R. Martinho Fernandes Dec 20 '11 at 09:34
  • Copying into `string`s also helps to get rid of the limited [modifiability](https://en.cppreference.com/w/cpp/language/main_function#Explanation) of the original `argv` elements. – Wolf May 09 '23 at 10:54
17

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 consts. 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.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • 1
    You're right, it isn't hard, but I didn't think of the problem like you did :D I thought the level of indirection with my 'char *arg' was wrong. Yeah, way off. Thanks for the tip and the code! – IAE Dec 20 '11 at 09:28
  • @SoulBeaver: _nod_ The -1 poster I think thought the same thing. But that error message is because the ranged-for construct is trying to figure out how to shoehorn your bare char ** into some sort of thing it knows how to deal with. The last resort is to try to call `being(thing)` and `end(thing)` to get something iterator-like to use. – Omnifarious Dec 20 '11 at 09:31
  • 1
    @SoulBeaver: Here's a nice SO answer that details the requirements on ranges you use for range-based for loops: http://stackoverflow.com/questions/2648878/range-based-for-statement-definition-redundancy - I believe the default definitions of the `begin` and `end` free functions try to call `x.begin()` and `x.end()` where `x` is their argument (a reference to the range). – Omnifarious Dec 20 '11 at 09:36
  • I included that plus another similar and hopefully useful question as a footer to my question so that other people can easily find and access the same knowledge :) – IAE Dec 20 '11 at 09:51
  • 5
    If Boost is available, you can instead do `auto argv_range = [](size_t len, char const *const ptr) { return boost::make_iterator_range(ptr, ptr+len); }` or an equivalent function. I use a lambda just to avoid spelling out the return type, which is `boost::iterator_range`. – Steve Jessop Dec 20 '11 at 10:13
  • 1
    This would make a nice template tool for any array where you know the length and the pointer to some type. template ptr_range {... There's still a lot of APIs out there that return pointers from new [] or malloc even. They should bless boost::make_iterator_range(ptr, ptr+len) maybe. – emsr Dec 20 '11 at 17:17
  • Sadly this won't work to me on Visual Studio 2010, this is from C++11 – John Boe Jun 10 '16 at 09:22
  • @JohnBoe - Well, range based for loops are from C++11 too, so even the original question isn't relevant to you. – Omnifarious Feb 07 '17 at 03:56
7

You can use C++20 std::span() or gsl::span() to make view for argv:

for (auto const arg : std::span(argv, argc)) {
    std::cout << arg << '\n';
}

Or similar, using Ranges (std::view::counted()):

for (auto const arg : std::views::counted(argv, argc)) {
    std::cout << arg << '\n';
}

or directly using std::ranges::subrange:

for (auto const arg : std::ranges::subrange{argv, argv+argc}) {
    std::cout << arg << '\n';
}
Monah Tuk
  • 200
  • 1
  • 8
6

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!

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
-1

I take you'd like the range based loop in order to not have to write that much..

so to annoy c++ purists, while at the same time not exactly answering your question, we'd like to post some nicely unsafe ways to do it without any std::vector or OO-wrapper-crap :)

for (;argc--; argv++) {
    printf(" %s \n ", *argv);
}

or

while (argc--) {
    printf(" %s \n ", *(argv++));
}

or even

while (*argv) {
    printf(" %s \n ", *(argv++));
}
johannes_lalala
  • 561
  • 4
  • 12
-1

argv is a double pointer, argv**

So where &x creates a reference to the element in the array, your *arg does not create a reference but rather is the same type as every element in argv.

Authman Apatira
  • 3,994
  • 1
  • 26
  • 33
  • -1: This is confusing, not relevant to the problem the OP is having, and possibly wrong. – Omnifarious Dec 20 '11 at 09:28
  • How is it not relevant? His syntax is wrong which is why he is having compile errors. Pointers and referencing were confused. Christ, people get a few K points and feel the need to rate down for no reason on SO nowadays. – Authman Apatira Dec 20 '11 at 09:31
  • Except, his syntax isn't wrong. The thing that's wrong is that `argv` is not a valid range. – Omnifarious Dec 20 '11 at 09:35
  • So you're telling me if I had used a different, self defined double pointer array besides argv, that code would have worked? If not, then correct me if im wrong, but that is the definition of a syntax error. – Authman Apatira Dec 20 '11 at 09:38
  • 2
    Well, not a double pointer array exactly. If you had a declaration like `char *argv[30];` it would worked perfectly. That because the compiler can detect a bound on `argv`. If you had `char *stuff[30]; char **argv = stuff;` it would've failed again with the same error. The problem has nothing to do with whether or not references are being used or anything like that. It's all about whether the thing given as the range can be construed as a valid range by the compiler. – Omnifarious Dec 20 '11 at 09:41
  • The syntax is fine. It's a semantic error. It can even be made to "work" even with `argv`. Just add `int* begin(char**) { return nullptr; } int* end(char**) { return nullptr; }` and it compiles. The syntax is fine. – R. Martinho Fernandes Dec 20 '11 at 09:49
  • @R.MartinhoFernandes: Err, `char **begin(char **) { return nullptr; }` is what I think you meant. :-) – Omnifarious Dec 20 '11 at 10:00
  • 1
    In support of the point @Omnifarious made, `char** argv[]` is not necessary actually. `char* argv[]` works fine too! – Subhamoy S. Jun 08 '12 at 14:47