C++26:牛津变长逗号
C++26: The Oxford Variadic Comma

原始链接: https://www.sandordargo.com/blog/2026/03/11/cpp26-oxford-variadic-comma

C++26 将弃用省略号参数(如 `printf` 中使用的参数),当省略号 (`...`) 前面*没有*逗号时。这一变化以牛津逗号命名,旨在提高与 C 语言的兼容性——C 语言*总是*要求使用逗号——并减少与现代 C++ 功能(如模板参数包)的混淆。 目前,C++ 允许 `void foo(int, ...)` 和 `void foo(int...)` 两种形式,而 C 语言仅接受前者。后一种形式是一种历史遗留问题,会导致歧义,尤其是在 `(T...)` 可能被误解为参数包而不是后跟省略号的单个类型。 甚至更奇怪的语法,如 `auto......` 在技术上是有效的,但极具误导性。 这是一个*弃用*,而不是移除,这意味着现有代码不会损坏。添加逗号是一个简单的机械转换,可以轻松地通过工具自动化。 虽然确切影响尚不清楚,但它为未来语言特性扫清了道路,这些特性之前由于这种歧义语法而被阻止,例如同质变长函数参数。最终,这是一个清理过程,可以提高一致性并为语言的未来做好准备。

sandordargo.com 上的一篇文章讨论了 C++26 中一项名为“牛津可变逗号”的提议,旨在提高可变模板的可读性——这是该语言中一个以复杂著称的部分。 目前,即使对于经验丰富的 C++ 开发者来说,可变模板也可能难以解析。这项提议旨在澄清与参数包相关的语法。 Hacker News 上的讨论指出,类似技巧曾被用于代码高尔夫竞赛中,利用实现细节(如堆栈顺序)来实现简洁但技术上未定义行为的代码。用户们赞赏这项提议更新带来的更清晰、更易理解的 C++ 代码潜力,尤其是在可变模板方面。
相关文章

原文

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.

The problem

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:

1
2
void foo(int, ...);  // with comma (C-compatible)
void foo(int...);    // without comma (C++-only)

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:

1
2
// In C, only this form is valid:
int printf(char*, ...);

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++.

Why is this confusing?

The real confusion comes from template parameter packs, introduced in C++11. Consider this example:

1
2
template<class Ts>
void f(Ts...); // well-formed: a parameter of type Ts followed by an ellipsis parameter

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:

1
2
template<class... Ts>
void f(Ts... args);  // args is a parameter pack

The situation gets even more confusing with abbreviated function templates:

1
2
3
4
5
// abbreviated variadic function template
void g(auto... args);

// abbreviated non-variadic function template with ellipsis parameter
void g(auto args...);

These two look similar but have completely different meanings. The latter should be deprecated.

The curious case of six dots

Perhaps the most bizarre syntax enabled by the current rules is this:

1
void h(auto......);  // equivalent to (auto..., ...)

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.

What’s being deprecated?

C++26 will deprecate ellipsis parameters without a preceding comma. Specifically:

1
2
3
4
5
6
7
8
9
// Deprecated in C++26:
void f(int...);
void g(auto args...);
template<class T> void h(T...);  // T is not a parameter pack

// Preferred (and C-compatible):
void f(int, ...);
void g(auto args, ...);
template<class T> void h(T, ...);

The standalone ellipsis parameter remains valid:

1
void f(...);  // still valid, C-compatible, unambiguous

Impact

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.

Conclusion

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.

Connect deeper

If you liked this article, please

联系我们 contact @ memedata.com