Подтвердить что ты не робот

Как написать диапазон на основе Loop с Argv?

С сайта С++ 0x Wikipedia:

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**&)’

Я использую Qt с g++ 4.6.1 на Ubuntu 11.10.

Дополнительная информация

Есть ли класс диапазона в С++ 0x

Резервирование заявлений на основе циклов на основе цикла

4b9b3361

Ответ 1

Вы этого не сделаете, потому что система не может определить, сколько времени 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 s. В принципе, argv - это массив указателей символов, ни один из которых не должен быть изменен, каждый из которых указывает на строку, ни один из символов которой также не должен быть изменен.

Ответ 2

Обычно первое, что я делаю с argc и argv, это:

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

Теперь у меня есть вектор строк для работы, и я могу легко использовать не только циклы, основанные на диапазонах, но и стандартные средства библиотеки С++.

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

Код wikipedia работает, потому что тип my_array является переменной типа массива. Исходный код не работает, потому что argv не является массивом. Синтаксис char* argv[] может сделать его похожим на массив, но это просто грустный артефакт синтаксиса C. char* argv[] точно совпадает с char** argv. argv не является массивом; это на самом деле просто указатель.

Цикл, основанный на диапазоне, работает на:

  • массивы;
  • любой тип, который имеет функции-члены begin() и end(), которые возвращают итераторы;
  • любой тип, для которого существуют нечлена-функции begin и end, которые могут быть вызваны как begin(x) и end(x), причем x является тем, что вы повторяете.

Как вы можете видеть, указатели не входят в этот список.

Ответ 3

Предлагаемое векторное решение копирует массив (только указатели, а не строки 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);

1 Это применимо только при использовании std::vector<char*>; если использовать std::vector<std::string>, как было предложено, даже сами строки скопированы!

Ответ 4

argv - двойной указатель, argv **

Итак, где & x создает ссылку на элемент в массиве, ваш * arg не создает ссылку, а скорее тот же тип, что и каждый элемент в argv.