Consider printf:
int printf ( const char * format, ... );
What are the terms used to describe the ... and the functions that use it? I've been calling it an ellipsis, but that's like calling & the "ampersand operator."
Variable length parameter list
Edit:
Or, if describing the function itself: Variadic function
Ellipsis notation (, ...) p202 "K+R The C Programming Language"
"Ellipsis" is in fact often the best term here. Sometimes we refer to "arguments passed using the ellipsis" (C++03 8.3.5p2). In the context of figuring out the best overloaded function, an argument can be said to "match the ellipsis" (C++03 13.3.2p2).
printf and other functions like it are often called "variadic functions".
Note: The coming C++0x Standard offers two different ways of declaring and implementing variadic functions (the va_arg way and the template way). But both involve the ellipsis token.
Ellipsis operator is the only term I have heard - it's rare enough (thankfully) that you don't need anything else!
This C++ draft specification refers to it simply as 'ellipsis' and sometimes with a definite or indefinite article, as 'an ellipsis' or 'the ellipsis'.
5.2.2 "Function call" section 6 contains:
A function can be declared to accept fewer arguments (by declaring
default arguments (8.3.6)) or more arguments (by using the ellipsis, ... 8.3.5)
than the number of parameters in the function definition (8.4).
8.3.5 "Functions" section 2 contains:
If the parameter-declaration-clause
terminates with an ellipsis, the
number of arguments shall be equal to
or greater than the number of
parameters that do not have a default
argument.
8.3.6 section 4 contains sample code:
void g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow
// a parameter with a default argument
Extra pedantry: section 13.3.3.1.3 ("Ellipsis conversion sequences") refers to "the ellipsis parameter specification". However, as stated in the sample code above, the ellipsis is not, strictly speaking, a parameter. 8.3.5 section 1 explains that, while the ellipsis appears in the parameter-declaration-clause, it follows the parameter-declaration-list.
In addition to "ellipsis" and "variadic function", one also sees the terms "vararg" and "varargs" thrown around. This appears to be an abbreviation for "variable argument list", judging by the language surrounding the (LEGACY) header <varargs.h> in POSIX.
Also, the principle reason that the term "ampersand operator" is not used is that the ampersand can represent either of two different operators, depending on the context, which would make the term ambiguous. This does not occur with the ellipsis; there is no other meaning assigned to it, so using the term "ellipsis" for the token "..." is not like using the term "ampersand operator" for the token "&".
Variadic
Martin and Demian are both right:
The three "." together form a ellipsis (On the Macintosh this is a single special character "...", but not usable for C++)
In C++ an ellipsis is used to define a Variable length parameter list
Related
Last week, I had a discussion with a colleague in understanding the documentation of C++ features on cppreference.com. We had a look at the documentation of the parameter packs, in particular the meaning of the (optional) marker:
(Another example can be found here.)
I thought it means that this part of the syntax is optional. Meaning I can omit this part in the syntax, but it is always required to be supported by the compiler to comply with the C++ standard. But he stated that it means that it is optional in the standard and that a compiler does not need to support this feature to comply to the standard. Which is it? Both of these explanations make sense to me.
I couldn't find any kind of explanation on the cppreference web site. I also tried to google it but always landed at std::optional...
The opt / (optional) suffix means the symbol is optional [for the C++ programmer to use; not the compiler to support]
As this question has been tagged language-lawyer, and as general when we look for a definite reference, let's move away from CppReference and into the standard.
Where CppReference uses the (optional) subscript, the standard uses opt; e.g. as in [temp.param]/1:
The syntax for template-parameters is:
template-parameter:
type-parameter
parameter-declaration
type-parameter:
type-parameter-key ...opt identifieropt
[... and so on]
[syntax]/1 describe the syntax notation [emphasis mine]:
In the syntax notation used in this document, syntactic categories are
indicated by italic type, and literal words and characters in constant
width type. Alternatives are listed on separate lines except in a few
cases where a long set of alternatives is marked by the phrase “one
of”. If the text of an alternative is too long to fit on a line, the
text is continued on subsequent lines indented from the first one.
An optional terminal or non-terminal symbol is indicated by the subscript “opt", so
{ expressionopt }
indicates an optional expression enclosed in braces.
Thus, you are correct, and your colleague is wrong. Particularly for your example of template parameter packs (which we introduce by the optional ... after typename) the identifier that follows after typename..., which names the pack (or the template parameter, if ... is omitted), is optional.
But he stated that it means that it is optional in the standard and that a compiler does not need to support this feature to comply to the standard.
The ridiculousness of this claim becomes even more clear if we annotate the "optional permutations" of a class template with a single type template parameter:
template<typename>
// ^^^^^^^^ type-parameter
// (omitting optional '...' and 'identifier')
struct S;
template<typename T>
// ^^^^^^^^^^ type-parameter
// (omitting optional '...')
struct S;
template<typename...>
// ^^^^^^^^^^^ type-parameter
// (omitting optional 'identifier')
struct S;
template<typename... Ts>
// ^^^^^^^^^^^^^^ type-parameter
struct S;
If the claim above was true, only the first of these four would need to be supported by a compliant implementation (based solely on grammar, in this contrived example), which would mean a compiler vendor could offer a compliant implementation where we could never name neither template (type) parameters nor function parameters.
It means that particular token is optional. For instance both these declarations work:
template <class... Args>
void foo();
template <class...>
void bar();
While I found a page that lists all of the marks, I was unable to find a page that specifies what the marks are intended to mean. Still, I might ask your colleague to take a look at some other pages, with the goal of the colleague abandoning the idea that "optional" means "optional to support". (This is not a definitive argument, but many would find it persuasive.) I found two good examples at Function declaration.
Function declaration:
noptr-declarator ( parameter-list ) cv(optional) ref(optional) except(optional) attr(optional)
Focus on cv (short for "const-volatile"), which is marked "optional" and which is "only allowed in non-static member function declarations". Your colleague's interpretation of this marker would mean that compilers do not have to support const member functions, as the const keyword is "optional".
Function definition, the first option for function-body:
ctor-initializer(optional) compound-statement
The "optional" part here is the member initializer list (only allowed in constructors). Is your colleague ready to claim that a compiler need not support member initializer lists?
Sometimes one should look at the familiar to understand annotations.
I understand that when an ellipsis (...) occurs to the right of a pattern containing a parameter pack, the pattern is expanded once for each parameter in the pack. However, though I have been able to find isolated examples of patterns with their expansion, I have been unable to find a definition of what constitutes a pattern. From what I can see, whitespace plays no role in the definition of the pattern, but parentheses do. For instance, in this example:
template<typename ... Ts>
void func(Ts)
{
do_something(validate(Ts)...);
}
the do_something line would be expanded to:
do_something(validate(var1), validate(var2), validate(var3))
if Ts happened to represent three variables. By contrast:
do_something(validate(Ts...));
would be expanded to:
do_something(validate(var1, var2, var3));
So clearly parentheses have something to do with determining where the pattern begins and ends. I can also see that whitespace does not. But that only gets me so far. I'd like to know exactly what constitutes a pattern, and how it will be expanded. I tried searching through the C++ Standard, but found too many instances of "parameter pack" to make that effective. Could someone please give me a definition of "pattern", or a link to a definition, or both?
UPDATE: To limit the scope of my question, I'd like to focus on the case where a pattern occurs within a function call. I've edited the title accordingly. Sorry I didn't make that clear from the beginning.
A pattern is defined in the standard under [temp.variadic]/4: (via #t.c.)
A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:
In a function parameter pack ([dcl.fct]); the pattern is the parameter-declaration without the ellipsis.
In a template parameter pack that is a pack expansion ([temp.param]):
if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;
if the template parameter pack is a type-parameter with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.
In an initializer-list ([dcl.init]); the pattern is an initializer-clause.
In a base-specifier-list (Clause [class.derived]; the pattern is a base-specifier.
In a mem-initializer-list ([class.base.init]) for a mem-initializer whose mem-initializer-id denotes a base class; the pattern is the mem-initializer.
In a template-argument-list ([temp.arg]); the pattern is a template-argument.
In a dynamic-exception-specification ([except.spec]); the pattern is a type-id.
In an attribute-list ([dcl.attr.grammar]); the pattern is an attribute.
In an alignment-specifier ([dcl.align]); the pattern is the alignment-specifier without the ellipsis.
In a capture-list ([expr.prim.lambda]); the pattern is a capture.
In a sizeof... expression ([expr.sizeof]); the pattern is an identifier.
In a fold-expression ([expr.prim.fold]); the pattern is the cast-expression that contains an unexpanded parameter pack.
The above quote from the standard draft talks about what part of the grammar described in the links is the "pattern" that is being expanded. To understand it, you need to know how the C++ grammar is described and the exceptions about how it is used in the standard text itself; however, if you have basic BNF knowledge and a bit of patience, you can work it out. The names are often useful as well.
However, you can use ... and mostly understand it without going nearly that deep.
The general rule is simple: you have some bit of C++ grammar (called the pattern) parsed in the usual way, where a pack of types is treated like a single type, and a pack of literals is treated like a single literal. Then, at the end of it, you have a ... expander. It then takes all of the unexpanded packs in the bit of C++ grammar immediately before (the pattern) and expands it.
How this works in each context is different; it isn't just a macro expansion. The contexts in which ... is valid are enumerated above; the effects of the expansion are listed in the standard at each point where it is valid.
In the most mundane use cases of ..., the pattern is an expression, and the expression gets expanded as-if it was each copy separated by , (not operator,, but the other "normal" one), usually in a context where a list of things is expected (function call, initializer list, etc).
There are function parameter declaration contexts (where ... both expands the types of the function parameters, and introduces a new pack of the paramter names), in template parameter lists (where it introduces a pack usually), etc.
sizeof... is a bit strange: for sizeof... it counts how many elements are in the pack passed in the (). This works differently, as the ... does not apply to the "structure on the left".
For alignas(X...), we end up with alignas(X#0), alignas(X#1), ... (where #0 is my pseudocode for the first element of the pack), as alignas(X#0, X#1, ...) is not valid C++ (again #T.C. in comments below).
For inheritance, it creates a set of base classes. For mem-initializer-lists, it lets you pass ctor arguments to said pack of base classes. For lambdas, it gives you limited capture of packs (not full on expression capture with expansion last I checked).
The pattern is the thing expanded. Importantly, an expanded pattern is not expanded by another pattern expander: so std::array< Ts, sizeof...(Ts) >... is a pack of arrays of various types, each one with a number of elements determined by how big the pack itself is. sizeof...(Ts) "counts as expanding" the Ts in its (), even though ... isn't to "its right", because the language defines the Ts in the () as the pattern that is expanded by the ....
The pattern in the general case cannot be called an expression, because types are not expressions, and some patterns are expressions (or at least expand into lists of expressions). And in some cases, ... expands a type-pattern into an expanded package of types (like in the throw expression).
The general rule, that you treat ... as expanding the thing on the left in an appropriate way in the local context, works for almost everything except sizeof... (which is a magical operator that tells you how many elements are in a parameter pack). It is only going to be in corner cases where this doesn't produce a decent model. And in my experience, at worse it will result in code that doesn't compile when you think it should; you can learn workarounds in that case. Like, remembering that a bare statement "has no local context", so you cannot do a = std::get<Is>(tup))...;, but rather the workaround (void)(int[]){0,(a = std::get<Is>(tup)),0)...}; (might have to typedef that int[]) where we provide a context (creating an array) for the pack expansion to work in.
C++1z's fold expressions are another quirky spot where ... does not apply on the left; here, they wanted to have an expansion of a binary operator, so "on the left" doesn't make as much sense as the usual "unary" expansion.
The pattern is defined by its association with the ... expansion syntax. So validate(ts) alone is just a fragment of meaningless text. But validate(ts)... makes the validate(ts) part the pattern of the expansion. So it's a matter of how ... affects the associated code.
Usually, unpack syntax designates that the stuff to the left of the ... is the pattern. With C++17's fold expressions, this becomes a bit more complex. But generally speaking, if you want to know what the pattern is, find the ..., and look at the expression immediately to the left of it.
The specific limitations on what can be in the pattern depend entirely on where the expansion is taking place and what kind it of pack is involved (type vs. non-type, etc). The pattern needs to be whatever would be legal code for whatever context it is expanded within.
How to work in C++ with functions the following form: void function(...) {}?
Do really need at least one implicit parameter?
Repeating from a comment:
It seems there is an interesting difference between C99 and C++11 here: C++11 allows a function declaration void foo(...) because the parameter-declaration-list in the parameter-declaration-clause is optional: [dcl.fct]
Function declaration:
D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt exception-specificationopt attribute-specifier-seqopt
Parameters:
parameter-declaration-clause:
parameter-declaration-listopt ...opt
parameter-declaration-list , ...
(Note how both the parameter-declaration-list and the ... are separately opt here, meaning you can leave out one or the other or both. This interpretation is supported by clang++ and g++.)
In C99, this declaration is not allowed since the parameter-list is not optional in the parameter-type-list: 6.7.5/1
Function declaration:
direct-declarator ( parameter-type-list )
Parameters:
parameter-type-list:
parameter-list
parameter-list , ...
As the va_start etc. macros/functions are inherited from C99, there's no way to use the arguments matched with the ellipsis with an empty parameter-declaration-list in C++.
Description of va_start in C99: 7.15.1.4
void va_start(va_list ap,parmN);
[...]
The parameter parmN is the identifier of the rightmost parameter in the variable
parameter list in the function definition (the one just before the , ...). [...]
Emphasis mine. C99 assumes there's a parameter before the ellipsis, because it isn't legal in C99 to declare a function with an ellipsis but without parameters.
Yet, I can see two reasons to use a function with an ellipsis but w/o any parameters in C++:
Overload resolution. Matching arguments to an ellipsis leads to a very low ranking of the overload: An ellipsis conversion sequence is worse than any user-defined and standard conversion sequence [over.ics.rank]/2. This can be useful for metaprogramming:
char foo(int);
int foo(...);
struct S{};
S s;
sizeof(foo(42)); // yields 1
sizeof(foo(s)); // yields sizeof(int)
Implementation-defined tricks. Your implementation may provide a mean to access those arguments matched with an ellipsis. E.g. see BobTFish's example
If you want to maintain some semblance of platform independence, you need at least one parameter and use the va_arg macros.
If you know the low level details of the architecture and calling convention, then you can pull the arguments directly out of the registers and/or stack (depending on where the various parameters end up).
Given that the va_start "function" requires an argument to set up the va_list, I'd say it's not possible to do this in a way that works reliably and portably. It may well be possible to find something that works on specific platforms - but don't expect it to work if you change the compiler, compile for a different platform, and in some cases even if you compile with different compiler options (or change the code in the function, e.g. introducing local variables).
...
va_list vl;
va_start(vl, arg);
...
Of course, your other problem is knowing when there is NO arguments (which would be a valid case). So if you don't have at least one argument, what happens when you pass no argument at all? How do you "know" this is the case?
As I study different sections in the C++ standard ( Where do I find the current C or C++ standard documents? ), I would like to refer back to the "Terms and definitions", §1.3.
However, the terms and definitions are provided in a form that I cannot adequately understand or interpret, and there is no explanation given in the text regarding how to interpret them.
Consider the very first term that is defined in the "Terms and definitions" section of the standard:
1.3.1 [defns.argument]
argument
actual argument
actual parameter
<function call expression> expression in the comma-separated list
bounded by the parentheses
What does [defns.argument] refer to?
What is the meaning and purpose of the lines actual argument and actual parameter?
Does <function call expression> refer to a different "term or definition"? If so, it's not defined in the "Terms and definitions" section - why not? If not, what does it refer to? (NOTE: I am not asking what "function call expression" means, because I already know; instead, I am asking how to read and interpret the "Terms and definitions" section of the C++ standard using this simple example.)
What does [defns.argument] refer to?
[defns.argument] is the section tag; it is intended to be used for editorial purposes as it is invariant under section renumbering (e.g. in response to insertion, removal or reordering of sections). It can also be used in referring to the standard, but section numbers (relative to a published version of the standard) are more concise.
What is the meaning and purpose of the lines actual argument and actual parameter?
"actual argument" and "actual parameter" are aliases for the term "argument". You will see below under 1.3.14 [defns.parameter] that "formal argument" and "formal parameter" are aliases for the term "parameter".
The terms "actual argument" and "actual parameter" only appear in [defns.argument]; "formal argument" is described as an alias in 8.3.5p11, and "formal parameter" is used in approximately 13 places, a small fraction of the number of places where "parameter" is used.
Does <function call expression> refer to a different "term or definition"?
The angle-bracketed term is the context in which this definition applies. For example, "argument" has a different meaning in the context of a "function call expression" to in the context of a "function-like macro".
What does [defns.argument] refer to?
That's an alternative way of referring to the section (1.3.1). It should remain the same in future versions of the standard (unless it's removed), while the numbering may change.
What is the meaning and purpose of "actual argument" and "actual parameter"?
They are other terms that you might see, that mean the same thing. I believe that old versions of the spec. used "formal argument" and "actual argument" where the modern spec. uses "parameter" and "argument".
Does <function call expression> refer to a different "term or definition"?
That's the context in which "argument" has this meaning - the following sections give it different meanings in other contexts. Function call expressions are defined in 5.2.2; within such an expression, "argument" means "expression in the comma-separated list bounded by the parentheses".
I've seen methods with the following signature:
void foo (void);
They take no argument, however I'm wondering whether doing this is useful or not. Is there a reason why you would want to do it?
This is a holdover from older versions of C, where foo() meant "a function with an unknown number of parameters" and foo(void) means "a function with zero parameters." In C++, foo() and foo(void) both mean "a function with zero parameters", but some people prefer the second form because it is more explicit.
The C++03 standard says (emphasis mine):
8.3.5.2
The parameter-declaration-clause determines the arguments that can be
specified, and their processing, when the function is called. [Note:
the parameter-declaration-clause is used to convert the arguments
specified on the function call; see 5.2.2. ] If the
parameter-declaration-clause is empty, the function takes no
arguments.
This means that if you are talking to the compiler it's just a matter of taste.
If you are writing code that will be read by others, then the C++ way of doing things is
void foo();
The other form remains valid only for reasons of compatibility with C, where there was a difference among the two signatures.
In C++ code there is no reason whatsoever to use void in this way. What's more it is very much not the idiomatic way to declare parameterless functions.
This is a legacy from the older versions of C for functions with no arguments.