3

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);
}
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
dme
  • 55
  • 5
  • 1
    A default value still needs the assignment, no matter what. – πάντα ῥεῖ Oct 14 '18 at 18:42
  • Once you get past your error, [This](https://stackoverflow.com/questions/18222926/why-is-list-initialization-using-curly-braces-better-than-the-alternatives#18222927) may help with initialization. – lakeweb Oct 14 '18 at 18:51
  • Post updated with a basic example. The code in the "duplicate" post seems to be marked as valid but the code in my post does not seem to be valid and I'd like to understand why this is so or if there is some background design reason as to why this is not valid C++ syntax. – dme Oct 15 '18 at 07:59
  • The “duplicate” doesn’t really answer this question. – Konrad Rudolph Oct 15 '18 at 08:26
  • Because providing default value is not about initialization, but parameter passing? – Jean-Baptiste Yunès Oct 15 '18 at 08:30
  • Thanks Jean-Baptiste....thinking about it from that angle does kind of make sense. – dme Oct 15 '18 at 08:55

1 Answers1

3

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 2
    Thanks StoryTeller.....that explanation certainly helps to settle the matter in my mind. Thank you for pointing to a specific example to clarify your point. – dme Oct 15 '18 at 08:54
  • 1
    "we'll just have another vexing parse" we already have so many, nobody will notice :P – Rakete1111 Oct 15 '18 at 20:12