如何用Argv编写基于范围的for循环

How to Write the Range-based For-Loop With Argv?

本文关键字:范围 循环 for 于范围 Argv 何用      更新时间:2023-10-16

来自c++0x维基百科网站:

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

那么为什么这段代码不能工作呢?

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

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

我在Ubuntu 11.10上使用Qt和g++ 4.6.1。

c++中是否有Range类

基于范围的for循环语句定义冗余

通常,我对argcargv做的第一件事是:

std::vector<std::string> arguments(argv, argv + argc);

现在我有了一个字符串向量来处理,我不仅可以很容易地使用基于范围的for循环,而且还可以使用c++标准库工具。

for(std::string& s : arguments) {
    // do stuff...
}

维基百科代码工作,因为my_array的类型是一个数组类型的变量。原来的代码不工作,因为argv不是一个数组。语法char* argv[]可能使它看起来像一个数组,但这只是C语法的一个可悲的产物。char* argv[] char** argv完全相同。argv不是数组;它实际上只是一个指针。

基于范围的for循环作用于:

  • 数组;
  • 具有返回迭代器的成员函数begin()end()的任何类型;
  • 任何存在非成员函数beginend的类型,可以像begin(x)end(x)一样调用,x是你迭代的东西。

如你所见,指针不属于这个列表。

您不知道,因为系统无法告诉argv在编译时有多长。有人可能会在标准中找到合适的章节来引用你的话。

有一种绕过它的方法,那就是创建一个自定义类来包装argv。其实也没那么难。

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_;
};

用法如下:

for (const char *arg: argv_range(argc, argv)) {
   // Do something.
}

是的,我用了很多const。基本上,argv是一个字符指针数组,其中任何一个都不应该被修改,每个都指向一个字符串,其中的任何字符也不应该被修改。

您可以使用c++ 20 std::span()gsl::span()argv制作视图:

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

或者类似的,使用Ranges (std::view::counted()):

for (auto const arg : std::views::counted(argv, argc)) {
    std::cout << arg << 'n';
}
或者直接使用std::ranges::subrange:
for (auto const arg : std::ranges::subrange{argv, argv+argc}) {
    std::cout << arg << 'n';
}

建议的向量解决方案复制数组(仅复制指针,不复制字符串1 -但仍然)。Unnessary。argv_range解决方案是我也会尝试的,如果我绝对想强制一个基于范围的循环。但是这会产生很多额外的代码(承认,只有一次,如果你把它写进头文件并保留它,但仍然)。

经典的循环对我来说太容易了,所以我允许自己把它贴出来,我不认为为了一个基于范围的循环而付出这么大的努力是值得的…

for (char** a = argv; *a; ++a)
{
    // use *a, or if you don't like:
    char* arg = *a;
    // use arg...
}

或者,如果你以后不再需要argv数组:

for (++argv; *argv; ++argv)
{
    // use *argv, or if you don't like:
    char* a = *argv;
    // use a...
}

有一点不同,您可能已经注意到:在第一个变体中,我遍历所有值,在第二个变体中,我省略了第一个(通常是我们在许多情况下不感兴趣的程序名)。反过来,对于每个

for (char** a = argv + 1; *a; ++a);
for (; *argv; ++argv);

请注意,所有这些变体都得益于字符串数组本身以空指针结束的事实,就像任何字符串都以空字符结束一样,因此*a*argv的简单检查。


1这只适用于使用std::vector<char*>;如果使用std::vector<std::string>,就像实际建议的那样,甚至字符串本身也会被复制!

argv是双指针,argv**

因此,当&x创建对数组中元素的引用时,*arg不会创建引用,而是与argv中的每个元素的类型相同。

我想你会喜欢基于范围的循环,这样就不用写那么多了。

所以为了惹恼c++纯粹主义者,同时不完全回答你的问题,我们想发布一些很好的不安全的方法来做到这一点,没有任何std::vector或oo包装垃圾:)

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

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

或者

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