2010-09-02 2 views
3

Какие недостатки я мог бы иметь, если я хочу использовать функциюФункция с переменными аргументами

foo(int num, ...) 

реализовать переменное число аргументов?

Я знаю первый недостаток, что вы можете использовать только один тип данных.

Есть ли еще способ сделать это?

+0

Тема с ошибками слова – leppie

+0

@leppie: Исправлено. –

ответ

2

Существует несколько способов НЕ использовать многоточие.

Почему? Из-за типобезопасности а опасные манипуляции примитивов (va_start, va_arg, va_next), что вы не можете действительно вперед к другой функции и т.д ...

Однако, в отличие от C, C++ предоставляет методы шаблонов, которые предлагают безопасность типа и общее поведение, и это может совмещаться с перегрузками:

template <typename Arg0> 
void foo(int num, Arg0 const& arg0); 

template <typename Arg0, typename Arg1> 
void foo(int num, Arg0 const& arg0, Arg1 const& arg1); 

// ... etc 

Это текущее состояние искусства, которое, как правило, помогли с помощью тонкого применения Препроцессор программирования (проверить Boost.Preprocessor).

С нового стандарта C++ 0x, приходят VARIADIC шаблоны, которые предлагают одни и те же объекты, чем методы С переменным числом, с типом безопасности, предлагаемой (Yeeha)

template <typename Arg0, typename... Args> 
void foo(Arg0 arg0, Args... args) 
{ 
    // Do something with arg0 
    foo(args); 
} 

template <typename Arg0> 
void foo(Arg0 arg0) 
{ 
    // Do something with arg0 
} 

Это также позволяет определить tuple классы гораздо проще:

+0

это кажется действительно хорошим решением. Тем не менее, я до сих пор не могу понять, как я могу использовать этот подход для решения произвольных параметров. , связанный с вашим кодом, как я могу вызвать функцию foo с произвольными параметрами – sami

+0

Поскольку это функция шаблона и, следовательно, она работает с несколькими различными типами. Из подписи, хотя мы отмечаем, что тип нужно копировать, но вы можете использовать 'Arg0 const &', если ваш usecase работает с ним. –

+0

есть ли какой-либо компилируемый пример, который демонстрирует вариационный шаблон !! – sami

4

См. this question. Самая большая проблема здесь - безопасность типа. Вы собираетесь извлекать параметры во время выполнения, а не во время компиляции. Вы,, реализуете для этого логику, а не компилятор. Это означает, что существует феноменально более высокая вероятность ошибки. Не только это, но ваш код будет заражен нерелевантной логикой, которая должна быть действительно сделана для вас компилятором. Вы не ограничены одним типом параметра, но тем не менее это не является преимуществом.

В C++ существует ряд альтернативных способов решения этой проблемы, многие из которых лучше всего подходят для обозначения многоточия. См. that other question за несколько идей. Классический пример может служить в C++ iostreams, в отличии от printf() из C:

std::cout << 'I' << " love h" << 3 << "r\n"; 

Ничего недостатков библиотеки, ее использование оператора вставки является одним ее ярчайшим использованием C++.

+0

@wihelmetell: Я согласен с тем, что вы сказали. О типе-безопасности, если вы знаете, что у вас только один тип данных. Есть ли проблема по типу безопасности? – sami

+0

Если компилятор знает тип значения, тогда нет необходимости бросать, и поэтому нет проблемы безопасности типа. Но в C++ компилятор не знает тип varargs, просто потому, что язык этого не гарантирует. C++ даже не предоставляет синтаксический способ сообщить компилятору, что последовательность varargs имеет один тип. Один тип или много, вам все равно нужно включить внутри вызываемой функции, и поэтому у вас есть проблема безопасности типов. – wilhelmtell

+0

Тем не менее, 'printf()' умнее любой другой функции в этом отношении. Компилятор понимает специальные токены в строке аргумента, если строка является литералом, и поэтому может проверять типы безопасности. Но это не стандартная гарантия, и вы не можете получить такие проверки компилятора в своем собственном коде, если вы используете varargs. – wilhelmtell

7

Вы не ограничены аргументами одного типа данных; printf() семейство функций в C (и C++) опровергает этот слух.

Основным недостатком обозначений с многоточием является то, что вы теряете безопасность типа; компилятор не может сказать вам, когда вы используете аргумент неправильного типа. (Язык программирования Go позволяет вам указать, что функция принимает произвольное количество параметров одного типа, что представляет интересную идею.)

Внутри функции должен быть какой-то способ показать, сколько аргументов были предоставлены и какие типы. Снова ссылаясь на printf(), строка формата сообщает ей, какие другие аргументы ожидаются. Современные компиляторы знают об этих строках формата и могут проверить, что приведенные аргументы соответствуют строке формата (когда строка формата является литералом). Это позволяет в какой-то мере сохранить безопасность, но это не будет доступно вам. Использование счетчика - один из способов его обработки - но тогда вам интересно, почему вы не используете vector<T> или что-то подобное для передачи данных. Другой классический способ - иметь значение маркера - обычно нулевой указатель - в конце список входов.

Таким образом, вам часто не нужен список вариационных аргументов. Когда вы его используете, вы обычно оставляете себя открытым, чтобы делать ошибки, которые избегают другие механизмы.

+0

+1 Кроме того, произвольное количество параметров одного типа --- то, что любое отличие от Java 5 varargs, иными словами, насколько интересно это сравнение по сравнению с последним? :-) –

+0

Java 5 фактически не имеет varargs. Все, что он делает, превращает их в массив. Это синтаксический сахар. В C или C++ вы также можете взять массив, и если вы хотите несколько типов, вы можете взять массив boost :: variant/any. – Puppy

+1

@Chris: так как я не знал о Java 5 [varargs] (http://www.deitel.com/articles/java_tutorials/20060106/VariableLengthArgumentLists.html), мне нужно было посмотреть и посмотреть, что он предлагает. Объект [Go] (http://golang.org/doc/go_spec.html#Passing_arguments_to_..._parameters) по существу изоморфен тому, что находится в Java 5. –

0
void myprintf(char* fmt, ...) 
{ 
    va_list args; 
    va_start(args,fmt); 
    vprintf(fmt,args); 
    va_end(args); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int a = 9; 
    int b = 10; 
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b); 
    return 0; 
} 
0

Порядочный C++ альтернатива-то вроде

foo(blah, ArgList(a)(b)(c));

где ArgList класс, который имеет перегруженный operator() и foo это функция, которая принимает ArgList. Это кратчайший способ передать переменные аргументы функции. В зависимости от ваших требований к разным типам вы можете спроектировать его, как хотите.

Или что-то вроде

foo(blah)(a)(b)(c);

где Foo является класс с перегруженным operator(). Здесь вы создаете временную, и деструктор будет вызываться после точки с запятой.

0

Вы можете использовать библиотеку Functor Loki, которая использует список типов для переменного количества аргументов для функции, которая также безопасна по типу.

0

В дополнение к проблемам безопасности, которые уже затронуты, вы не можете передавать типы не-POD как vararg вообще.

Смежные вопросы