C++中函数默认参数值根据参数名称而定

46

如果在C++中定义了一个新变量,那么可以在初始化表达式中使用该变量的名称,例如:

int x = sizeof(x);

那么函数参数的默认值呢?在那里可以使用参数名称引用参数吗?例如:

And what about default value of a function argument? Is it allowed there to reference the argument by its name? For example:

void f(int y = sizeof(y)) {}

这个函数在Clang中被接受,但在GCC中被拒绝,并出现错误:

'y' was not declared in this scope

演示: https://gcc.godbolt.org/z/YsvYnhjTb

哪个编译器是正确的?


重复的问题:【在参数默认值中使用它自己的名称-这合法吗?】 - Language Lawyer
谢谢提供参考,这确实是一个非常相似的问题。虽然这个问题的答案似乎更加详细。 - Fedor
2个回答

35
根据C++17标准(11.3.6 默认参数)
9 每次函数被调用时都会计算默认参数,如果对应的参数没有参数传入。一个参数不得作为默认参数中的潜在评估表达式出现。在默认参数之前声明的函数参数处于作用域内,并且可以隐藏命名空间和类成员名称。
该标准提供了以下示例:
int h(int a, int b = sizeof(a)); // OK, unevaluated operand

所以,这个函数声明

void f(int y = sizeof(y)) {}

sizeof(y) 在这个表达式中是正确的,因为根据 C++17 8.3.3 Sizeof:

1 sizeof 运算符返回其操作数的对象表示中的字节数。 操作数可以是一个未求值的操作数表达式(第8条),或者是一个带括号的类型标识符。

以及 C++17 6.3.2 Point of declaration:

1 名称的声明点在其完整的声明符(第11条)之后、其初始化程序(如果有的话)之前,除非下面有所说明。


1
但是你如何解释gcc给出的错误呢?或者这是gcc的一个bug吗? - Kshitij Joshi
11
这是编译器的一个错误。 - Vlad from Moscow

9

代码看起来没有错误,所以Clang没问题。

[basic.scope.pdecl]

1 名称的声明点是在其完整声明符([dcl.decl])之后、其初始化程序(如果有的话)之前,除非下面有说明。

这就是正在讨论的臭名昭著的段落。我在这里提到“除非下面有说明”,并没有包括任何默认参数的提及。因此y= sizeof(y)之前被声明。

另一个相关段落是

[dcl.fct.default]

9 每次调用函数时,都会对默认参数进行求值,而不为相应的参数提供参数。一个参数不能出现在默认参数的可能计算表达式中。在默认参数之前声明的函数参数在作用域内,并且可以隐藏命名空间和类成员名称。

sizeof(y)不会被潜在地评估,所以这也是可以的。

既然第一段将y作为名称可用,并且以非非法方式使用了它,那么这必须是GCC拒绝代码的一些怪癖。

尽管个人认为,这并不是最实用的代码。


7
注意,在C++11中有一个不同的规定:“函数的参数不能在默认参数中使用,即使它们没有被评估”。链接:https://timsong-cpp.github.io/cppwp/n3337/dcl.fct.default#9。 - Daniel Langr
@DanielLangr - 有趣。可能是g++前端的剩余部分。不过它确实接受标准的示例。 - StoryTeller - Unslander Monica
我认为他们应该只是移除限制,让编译器根据需要对默认参数进行排序,以便在其他参数可能被评估使用时简化使用。 - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接