乘暇再更一个 C++26 的小特性,动机简单,考虑以下例子:
1using Point3D = std::tuple<float, float, float>;
2
3int main() {
4 auto [x, y, z] = Point3D(1, 2, 3);
5
6 return x;
7}
y
和
z
皆未使用,可仍旧要起一个名称,实显多余。新特性允许使用
_
作为占位符,忽略不关心的值。于是可以这样写:
1using Point3D = std::tuple<float, float, float>;
2
3int main() {
4 auto [x, _, _] = Point3D(1, 2, 3);
5
6 return x;
7}
除了
Structured binding
,RAII 也是一个常用的场景。
1int main() {
2 std::mutex mtx;
3 std::lock_guard _(mtx);
4 std::ifstream _;
5 std::unique_ptr<int> _;
6}
允许重定义,所以全部写在一起也不会存在问题。但是,重定义之后无法使用,Name Lookup 会出现歧义。
1void g() {
2 int _;
3 _ = 0; // OK
4 int _; // OK, name independent declaration
5 _ = 0; // error: two non-function declarations in the lookup set
6}
7
8void h () {
9 int _; // #1
10 _ ++; // OK
11 static int _; // error: conflicts with #1 because static variables are not
12 // name-independent
13}
14
15int main() {
16 int _{}; // equivalent to [[maybe_unused]] int _;
17 float _{};
18 double _{};
19 char _{};
20
21 return _; // ambiguous
22}
这个占位符本身的目的就是避免为不准备使用的变量命名,所以这不是问题。此外,使用
_
还会隐式
[[maybe_unused]]
,因此编译器不会给出任何警告。
需要注意,允许使用这种占位符命名的变量,包含
automatic storage duration
、非静态成员变量、
Structured binding
和
lambda capture
。其他情况并不满足,比如
NTTP
、
requires clause
参数名和函数参数。
一个例子:
1namespace a {
2 auto _ = f(); // Ok, declare a variable "_"
3 auto _ = f(); // error: "_" is already defined in this namespace scope
4}
5void f() {
6 auto _ = 42; // Ok, declare a variable "_"
7 auto _ = 0; // Ok, re-declare a variable "_"
8 {
9 auto _ = 1; // Ok, shaddowing
10 assert( _ == 1 ); // Ok
11 }
12 assert( _ == 42 ); // ill-formed: Use of a redeclared placeholder variables
13}
14
15int main() {
16
17}
这里也出现了 Names shadow,和重载决议里面提到的意义相同,所以它能够访问。
这个小特性并不稀奇,其他语言中早已有之。比如 Python:
1