46

If one defines a new variable in C++, then the name of the variable can be used in the initialization expression, for example:

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)) {}

This function is accepted in Clang, but rejected in GCC with the error:

'y' was not declared in this scope

Demo: https://gcc.godbolt.org/z/YsvYnhjTb

Which compiler is right here?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Fedor
  • 17,146
  • 13
  • 40
  • 131
  • Dup of [Using a parameter's name inside its own default value - is it legal?](https://stackoverflow.com/questions/40513156/using-a-parameters-name-inside-its-own-default-value-is-it-legal) – Language Lawyer Feb 10 '22 at 09:56
  • Thanks for the reference, it is indeed a very similar question. Although the answers in this one seem more detailed. – Fedor Feb 10 '22 at 16:30

2 Answers2

35

According to the C++17 standard (11.3.6 Default arguments)

9 A default argument is evaluated each time the function is called with no argument for the corresponding parameter. A parameter shall not appear as a potentially-evaluated expression in a default argument. Parameters of a function declared before a default argument are in scope and can hide namespace and class member name

It provides the following example:

int h(int a, int b = sizeof(a)); // OK, unevaluated operand

So, this function declaration

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

is correct because, in this expression sizeof(y), y is not an evaluated operand, based on C++17 8.3.3 Sizeof:

1 The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause 8), or a parenthesized type-id.

and C++17 6.3.2 Point of declaration:

1 The point of declaration for a name is immediately after its complete declarator (Clause 11) and before its initializer (if any), except as noted below.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
9

The code does not appear ill-formed, so Clang is alright.

[basic.scope.pdecl]

1 The point of declaration for a name is immediately after its complete declarator ([dcl.decl]) and before its initializer (if any), except as noted below.

This is the notorious passage that is under discussion. I bring it here just to mention that "except as noted below" doesn't include any mention of default arguments. So y is declared right before = sizeof(y).

The other relevant paragraph is

[dcl.fct.default]

9 A default argument is evaluated each time the function is called with no argument for the corresponding parameter. A parameter shall not appear as a potentially-evaluated expression in a default argument. Parameters of a function declared before a default argument are in scope and can hide namespace and class member names.

sizeof(y) is not potentially evaluated, so this is also fine.

Seeing as the first paragraph makes y available as a name, and it's used in a way that is not illegal, must be some quirk of GCC that rejects the code.

Though personally, I don't see it as a great loss. This is not the most practical bit of code.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 7
    Note that in C++11, there is a different formulation: _"parameters of a function shall not be used in a default argument, even if they are not evaluated"_. Link: https://timsong-cpp.github.io/cppwp/n3337/dcl.fct.default#9. – Daniel Langr Oct 06 '21 at 07:53
  • @DanielLangr - Interesting. May be a left over in g++'s frontend. It does accept the standard's examples, though. – StoryTeller - Unslander Monica Oct 06 '21 at 08:00
  • I think they should have just removed the restriction, and let the compiler order the defaulted arguments as needed for potentially-evaluated use of other arguments. Would have simplified use at least. – Deduplicator Oct 07 '21 at 12:28