Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uniform Initialization inside constructor/function declaration parameter list

I am learning some basic things about classes and OOP in C++. From what I read the preferred modern way of initializing a variable is to use uniform initialization.

In the simple class header example below, uniform initialization is used to initialize the 3 data members (length, width and height).

For consistency, I thought it might be a good idea to use uniform initialization when setting default values in the constructor declaration but this does not work and the compiler (gcc 6.3 on Debian Stretch) generates and error. From what I can see the compiler thinks the curly braces {} are the start of the constructor definition body (clearly it is not since the right bracket ")" has not yet been added).

I accept this will not work but out of curiosity is there a reason why? I would prefer to be consistent with my code and use uniform initialization where ever possible.

Thanks.

#ifndef BOX_H
#define BOX_H

class Box
{

private:
    double length {1.0};
    double width {1.0};
    double height {1.0};

public:
    //constructor
    Box(double l = 1.0, double w = 1.0, double h = 1.0); //ok
    //Box(double l {1.0}, double w {1.0}, double h {1.0}); //Error

    double volume();
};

#endif

EDIT....thanks for the comments so far but I'm not sure I understand the reason why you cannot use uniform initialization for default arguments. Is there some standard C++ documentation someone can point me to?

For example, taking the basic program below, it is ok to initialize n with a value of 5 using uniform initialization but it is not ok to initialize x like this as a default argument in the function header (I am using gcc 6.3 with -std=c++17). Why is this? Apologies if I have not understood the help so far.

#include <iostream>

void printNum(int x {1}) //error
{
    std::cout<<x<<"\n";
}

int main()
{
    int n {5};  //OK

    printNum(n);
}
like image 840
dme Avatar asked Aug 02 '26 21:08

dme


1 Answers

It's a grammatical constraint. This specific grammar element is described in [dcl.fct] ¶3:

parameter-declaration-clause:
  parameter-declaration-listopt ...opt
  parameter-declaration-list , ...

parameter-declaration-list:   
  parameter-declaration
  parameter-declaration-list , parameter-declaration

parameter-declaration:    
  attribute-specifier-seqopt decl-specifier-seq declarator 
  attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
  attribute-specifier-seqopt decl-specifier-seq abstract-declaratoropt
  attribute-specifier-seqopt decl-specifier-seq abstract-declaratoropt = initializer-clause

The thing to note is that the grammar permits an initializer-clause to be present only if it's preceded by a =. I suspect it's to ensure there's no ambiguity between function declarations and object declarations. For instance, in block scope:

Widget foo(CompA{}, CompB{});

Has to be an object declaration, like the uniform initialization proposal wanted to make sure. Allowing plain {} as a default argument would make the above ambiguous, and we'll just have another vexing parse to add to the collection. So a = is required.

Now, as for why not allow it in a constructor where ambiguity is unlikely: the standard committee is not really in the habit of allowing a very constrained use-case if the more general one cannot be supported.

like image 97
StoryTeller - Unslander Monica Avatar answered Aug 05 '26 09:08

StoryTeller - Unslander Monica



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!