Как написать диапазон на основе 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
Резервирование заявлений на основе циклов на основе цикла
Ответы
Ответ 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.