Function with variable arguments
Which disadvantages could I have if I want to use the functio开发者_运维技巧n
foo(int num, ...)
to implement the variable number of arguments?
I do know the first disadvantage that you can only use one data type.
Is there any way else to do that?
You are not restricted to arguments of one data type; the printf()
family of functions in C (and C++) belies that rumour.
The primary disadvantage of the ellipsis notation is that you lose type safety; the compiler cannot tell you when you are using an argument of the wrong type. (The Go programming language allows you to specify that a function takes an arbitrary number of parameters of a single type - which is an interesting idea.)
Inside the function, there must be some way for it to tell how many arguments were provided and what the types are. Referring back to printf()
again, the format string tells it what other arguments are expected. Modern compilers know about these format strings and can check that the arguments given match the format string (when the format string is a literal). This allows for some type safety after all - but that won't be available to you. Using a count is one way of handling it - but then you wonder why you aren't using a vector<T>
or something similar to pass the data in. Another classic way is to have a marker value - typically a null pointer - at the end of the list of inputs.
So, you often don't need the variadic argument list. When you do use one, you typically leave yourself open to making errors that other mechanisms avoid.
See this question. The biggest problem here is type-safety. You're going to extract the parameters at runtime rather than compile-time. You will implement the logic for that, rather than the compiler. This means that there's a phenomenally higher chance for error. Not only that, but your code is going to be infested with irrelevant logic that should really be done for you by the compiler. You are not limited to one parameter type, but nevertheless this is not an advantage.
In C++ there are a number of alternative ways to go about this, many of which are better in every way than the ellipsis notation. See that other question for a few ideas. The classical example is in the C++ iostreams
, in contrast of printf()
of C:
std::cout << 'I' << " love h" << 3 << "r\n";
Never mind the flaws of the library, its use of the insertion operator is one its brightest uses of C++.
There are multiple ways NOT to use ellipsis notation.
Why ? Because of type safety a hazardous manipulations of the primitives (va_start
, va_arg
, va_next
) that you can't really forward to another function etc...
However, contrary to C, C++ provides template methods, which offer type safety and generic behavior, and this can be cumulated with overloads:
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
This is the current state of the art, which is generally helped by a subtle application of Preprocessor Programming (check out Boost.Preprocessor).
With the new C++0x standard, come the variadic templates, which offer the same facilities than the C variadic methods, with type safety offered (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
}
This also allows to define tuple
classes much more easily :)
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;
}
A decent C++ alternative is something like
foo(blah, ArgList(a)(b)(c));
where ArgList
is a class which has an overloaded operator ()
, and foo
is a function which takes a ArgList
. This is a concise way to pass variable arguments to a function. Depending on your requirements for different types you can design it however you want.
Or something like
foo(blah)(a)(b)(c);
where foo is a class with overloaded operator ()
. Here you create a temporary, and the destructor will be called after the semicolon.
You can use Loki's Functor Library that uses type list for variable number of argument to a function that is type safe as well.
In addition to the type safety issues already touched on, you can't pass non-POD types as a vararg at all.
精彩评论