Turns out, these two are equivalent in practice (but UB in the C++ standard):
double solve(double a, double b, double c, double d) {
return a + b + c + d;
}
double solve(double a ...) {
return a + 1[&a] + 2[&a] + 3[&a];
}At least they managed to kill `auto_ptr`.
Not in the x86-64 SysV ABI they aren’t. The arguments will be passed in registers (yes, even the variadic ones), so how your compiler will interpret 1[&a] is anybody’s guess. (For me, x86_64-unknown-linux-gnu-g++ -O2 yields, essentially, return a+a+a+a; which is certainly an interpretation. I’m also getting strange results from i686-unknown-linux-gnu-g++ -O2, but my x87 assembly is rusty enough that I don’t really get what’s going on there.)
double solve(double a[]) {
return 0[a] + 1[a] + 2[a] + 3[a];
}
solve((double[]){1, 2, 3, 4});
The cast in the invocation can be macro-ed away. And the best thing is, the actual stack layout and data movement/shuffling is pretty much identical to the approach with <stdargs.h>, and with no UB or compiler intrinsics. double solve(double a,double b,double c,double d){return a+b+c+d;}
double solve(double a...){return a+1[&a]+2[&a]+3[&a];}
double solve(a,b,c,d)double a,c,b,d;{return a+b+c+d;}I love C, but C++ has worthwhile advantages even if you heavily restrict which features you use.
All languages have some spikier edges, there are no languages I know where I think "Well, even if we could start over I have no idea how to improve this". What's notable about C++ is just how rich that "could do better" seam is, so very many of their defaults are wrong, so many of their keywords are the wrong word, so many of their standard library features are allowed, sometimes even mandated to be crap.
C++26 brings us a small but meaningful cleanup to the language: deprecating ellipsis parameters without a preceding comma. This change, proposed in P3176R1, aims to improve C compatibility, reduce confusion, and pave the way for future language features.
The proposal’s name is a playful reference to the Oxford comma - that final comma before “and” in a list. Just as the Oxford comma clarifies lists in English, this proposal mandates a comma before the ellipsis in function parameters.
First, let’s clarify terminology. This proposal is about ellipsis parameters - the C-style variadic parameters you might know from printf. These are different from template parameter packs, even though both use the ... syntax.
Currently, C++ allows two ways to declare an ellipsis parameter:
<table><tbody><tr><td><pre>1 2 </pre></td><td><pre><span>void</span> <span>foo</span><span>(</span><span>int</span><span>,</span> <span>...);</span> <span>// with comma (C-compatible)</span> <span>void</span> <span>foo</span><span>(</span><span>int</span><span>...);</span> <span>// without comma (C++-only)</span> </pre></td></tr></tbody></table>
The second form without the comma originates from early (pre-standard) C++ function prototypes, but has remained part of standardized C++ ever since. Interestingly, C has never allowed the comma to be omitted. The C standard, unchanged since C89, requires:
<table><tbody><tr><td><pre>1 2 </pre></td><td><pre><span>// In C, only this form is valid:</span> <span>int</span> <span>printf</span><span>(</span><span>char</span><span>*</span><span>,</span> <span>...);</span> </pre></td></tr></tbody></table>
C++ later added support for the comma-separated form for C compatibility, but kept the old syntax for backwards compatibility. This creates an awkward situation where (int, ...) is compatible with both languages, but (int...) only works in C++.
The real confusion comes from template parameter packs, introduced in C++11. Consider this example:
<table><tbody><tr><td><pre>1 2 </pre></td><td><pre><span>template</span><span><</span><span>class</span> <span>Ts</span><span>></span> <span>void</span> <span>f</span><span>(</span><span>Ts</span><span>...);</span> <span>// well-formed: a parameter of type Ts followed by an ellipsis parameter</span> </pre></td></tr></tbody></table>
Many users associate (T...) with a parameter pack, not with an ellipsis parameter. Instead, it’s a single parameter of type Ts followed by an ellipsis parameter! To actually declare a parameter pack, you need:
<table><tbody><tr><td><pre>1 2 </pre></td><td><pre><span>template</span><span><</span><span>class</span><span>...</span> <span>Ts</span><span>></span> <span>void</span> <span>f</span><span>(</span><span>Ts</span><span>...</span> <span>args</span><span>);</span> <span>// args is a parameter pack</span> </pre></td></tr></tbody></table>
The situation gets even more confusing with abbreviated function templates:
<table><tbody><tr><td><pre>1 2 3 4 5 </pre></td><td><pre><span>// abbreviated variadic function template</span> <span>void</span> <span>g</span><span>(</span><span>auto</span><span>...</span> <span>args</span><span>);</span> <span>// abbreviated non-variadic function template with ellipsis parameter</span> <span>void</span> <span>g</span><span>(</span><span>auto</span> <span>args</span><span>...);</span> </pre></td></tr></tbody></table>
These two look similar but have completely different meanings. The latter should be deprecated.
Perhaps the most bizarre syntax enabled by the current rules is this:
<table><tbody><tr><td><pre>1 </pre></td><td><pre><span>void</span> <span>h</span><span>(</span><span>auto</span><span>......);</span> <span>// equivalent to (auto..., ...)</span> </pre></td></tr></tbody></table>
Yes, that’s six consecutive dots - if I counted it right. This declares a function template parameter pack followed by an ellipsis parameter. While technically possible to use (if the pack belongs to a surrounding class template), the syntax strongly suggests all dots apply to auto, which is misleading.
C++26 will deprecate ellipsis parameters without a preceding comma. Specifically:
<table><tbody><tr><td><pre>1 2 3 4 5 6 7 8 9 </pre></td><td><pre><span>// Deprecated in C++26:</span> <span>void</span> <span>f</span><span>(</span><span>int</span><span>...);</span> <span>void</span> <span>g</span><span>(</span><span>auto</span> <span>args</span><span>...);</span> <span>template</span><span><</span><span>class</span> <span>T</span><span>></span> <span>void</span> <span>h</span><span>(</span><span>T</span><span>...);</span> <span>// T is not a parameter pack</span> <span>// Preferred (and C-compatible):</span> <span>void</span> <span>f</span><span>(</span><span>int</span><span>,</span> <span>...);</span> <span>void</span> <span>g</span><span>(</span><span>auto</span> <span>args</span><span>,</span> <span>...);</span> <span>template</span><span><</span><span>class</span> <span>T</span><span>></span> <span>void</span> <span>h</span><span>(</span><span>T</span><span>,</span> <span>...);</span> </pre></td></tr></tbody></table>
The standalone ellipsis parameter remains valid:
<table><tbody><tr><td><pre>1 </pre></td><td><pre><span>void</span> <span>f</span><span>(...);</span> <span>// still valid, C-compatible, unambiguous</span> </pre></td></tr></tbody></table>
This is a pure deprecation - removal had been already refused before. No existing code becomes ill-formed. Any deprecated uses can be mechanically transformed by adding a comma before the ellipsis. This transformation is simple enough to be automated by tooling.
The proposal doesn’t estimate how much code will be affected, though the author found several dozen occurrences of the T...... pattern in a GitHub code search. The real number of affected declarations is likely non-trivial, finding them requires semantic analysis since (T...) could be either an ellipsis parameter or a parameter pack depending on context.
This deprecation clears the path for future language features. The syntax (int...) has already blocked proposals like P1219R2 “Homogeneous variadic function parameters”, which would have given this syntax a new meaning.
By deprecating the comma-less form, the committee preserves design space for future evolution while improving consistency with C and reducing a common source of confusion.
The Oxford variadic comma is a small change with multiple benefits: better C compatibility, reduced confusion with parameter packs, and preserved design space for future features. While the title is playful, the motivation is serious - cleaning up a historical artifact that serves little purpose in modern C++.
If you’re using ellipsis parameters, start adding that comma before the .... Your code will be more C-compatible, less confusing, and ready for whatever comes next.
If you liked this article, please