Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the default argument for a function parameter is considered as an initializer for that parameter?

Suppose I have function declarations like these:

static const int R = 0;
static const int I = 0;

void f(const int& r = R);
void g(int i = I);

Per [dcl.fct.default]/1:

If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument [..]

and per the grammar constructs, an initializer can comprise an initializer-clause. Right?

So I concluded that R is an initializer for the parameter r, and I is also an initializer for the parameter i.

Now per [const.expr]/2:

A variable or temporary object o is constant-initialized if

  • (2.1) either it has an initializer [..] and
  • (2.2) the full-expression of its initialization is a constant expression [..]

So both parameters have an initializer and also the full-expression of their initializations is a constant expression.

So, Are both parameters r and i considered constant-initialized?

like image 704
mada Avatar asked May 21 '26 07:05

mada


1 Answers

No, because an initializer-clause is not necessarily part of an initializer. The grammar item in question here is this version of the parameter-declaration:

attribute-specifier-seqopt thisopt decl-specifier-seq declarator = initializer-clause

Applying just the right amount of hair-splitting, this means that the form of a parameter declaration with a default argument is similar to an initialization, but it is still subtly different. The most obvious difference being that the default argument is ignored if an actual argument is used. Less obvious is that a parameter cannot actually be constant-initialized, even if its default argument is a constant expression. This remains true even if the function is only evaluated at compile time. Here is some code that shows the subtle differences in meaning:

#include <random>

// R may be evaluated at compile time and must be constant-initialized
static constexpr int R = 0;

// f may be evaluated at compile time
constexpr int f(const int& r = R) { return r + 42; }

// I must be constant- *or* zero-initialized, even if only evaluated at runtime
constinit const int I = f();

// g is an "immediate function": it may not be evaluated at runtime
consteval int g(int i = I) { return i - 42; }

int main() {
    // A variable that may not appear in constant expressions,
    // because it is not constant-initialized
    const int not_const = std::rand();

    static int x1 = f();                // OK, constant initialization
    // constexpr int x2 = f(not_const); // error, not constant-initialized
    int x3 = f();                       // OK, f() evaluated at compile time
    int x4 = f(not_const);              // OK, evaluated at runtime

    static int y1 = g();                // OK
    // constexpr int y2 = g(not_const); // error
    int y3 = g();                       // OK
    // int y4 = g(not_const);           // error
}

As you can see here, this generates only one runtime call to f(int const&) and none to g(int). Even though the parameters for the other evaluations were effectively compile-time constants, you can neither use nor specify them as such:

constexpr int f(const int& r = R) {
    // constexpr int x = r; // error, not a constant expression
    return r + 42;
}

consteval int g(int i = I) {
    // constexpr int y = i; // still not a constant expression!
    return i - 42; 
}

// Also not allowed:
// consteval void h(constexpr int x = 0, constinit int y = 1); 
like image 118
sigma Avatar answered May 23 '26 22:05

sigma



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!