Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference initialization forms

So I was testing some of the reference initialization forms, described here. I wonder when the:

T & ref = { arg1, arg2, ... };

and

T && ref = { arg1, arg2, ... };

forms will be ever used and for what exactly. I suppose it's for initializing temporary arrays and constructors with 'initializer_list', like this:

int main()
{


    struct _ab
    {
        _ab() {cout << "_ab()" << endl;}

        _ab(initializer_list<int> iArr) : a(*iArr.begin()), b(*iArr.end()) {cout << "_ab()" << endl;}

        ~_ab() {cout << "~_ab()" << endl;}
        int a, b;
    };
    const _ab & i = {1, 2};

    cout << i.a << endl;

    return 0;
}

In this context I tried to initialize the const reference with a temporary '_ab' object using the default constructor like this:

int main()
    {


        struct _ab
        {
            _ab() {cout << "_ab()" << endl;}

            _ab(initializer_list<int> iArr) : a(*iArr.begin()), b(*iArr.end()) {cout << "_ab()" << endl;}

            ~_ab() {cout << "~_ab()" << endl;}
            int a, b;
        };
        const _ab & i(); // error 1

        cout << i.a << endl; // error 2

        return 0;
    }

But this example didn't compile with 2 errors:

error 1: 'const main()::_ab& i()', declared using local type 'const main()::_ab', is used but never defined [-fpermissive]|

error 2: request for member 'a' in 'i', which is of non-class type 'const main()::_ab&()'|

Can you enlighten me about what exactly the above 2 construct means & are used for.

EDIT: I understand the problem with the second example. It was declaring a function instead of variable. But still can somebody explain me why references have the possibility to be initialized with initialization list and for what is it used?

like image 550
AnArrayOfFunctions Avatar asked Mar 17 '23 19:03

AnArrayOfFunctions


2 Answers

const _ab & i();

The standard explains this in a note in [dcl.init]/8:

[ Note: Since () is not permitted by the syntax for initializer,

X a();

is not the declaration of an object of class X, but the declaration of a function taking no argument and returning an X.

This applies regardless of the type and is referred to as the most vexing parse. Of course i.a is thus ill-formed.


But still can somebody explain me why references have the possibility to be initialized with initialization list and for what is it used?

[dcl.init]/3:

List-initialization of an object or reference of type T is defined as follows:

  • [..]

  • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.
    [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note ]

The last note refers to the fact that

int& i = {1};

Is ill-formed since we would have to bind 1 to a non-const lvalue reference, which isn't possible. Neither would

std::string& s = {20, '5'};

be valid since we have to initialize a non-const lvalue reference with a temporary.
The standard then gives examples:

struct S {
    S(std::initializer_list<double>); // #1
    S(const std::string&);            // #2
    // ...
};

const S& r1 = { 1, 2, 3.0 };    // OK: invoke #1
const S& r2 { "Spinach" };      // OK: invoke #2
S& r3 = { 1, 2, 3 };            // error: initializer is not an lvalue
const int& i1 = { 1 };          // OK
const int& i2 = { 1.1 };        // error: narrowing
const int (&iar)[2] = { 1, 2 }; // OK: iar is bound to temporary array

This can also be particularly useful for function calls.

void f(std::vector<int> const&);

f( {first, last} );
like image 199
Columbo Avatar answered Mar 28 '23 23:03

Columbo


const _ab & i();

Above code declares (and calls) a function that returns a const reference to _ab struct - that's the first error. The second error is that you try to access a member variable of a function (i), which is not possible.

To use default constructor you could use the new {...} C++11 syntax, it should work in your example

const _ab & i {}; // probably will work fine

however omiting the braces will lead to an error - this one will not work:

const _ab & i;  // error

This should also work fine:

const _ab & i = {};

EDIT:

You can initialize reference with an "anonymous" object (like in all your examples) only if that is a const reference. It's allowed in the language, I'm not sure there's any deeper reason for that and why it shouldn't be allowed. So you don't actually initialize reference with initializer list (or whatever) - these are used to initialize an anonymous object, and this anonymous object is used as a "target" for the const reference. If you'd declare the constructor to be explicit, you'd need to specify the type, so maybe in that case it wouldn't be so confusing as for what the initializer is actually used:

const _ab & i = _ab{/*...*/};

This is equivalent to:

const _ab anonymousObject = _ab{/*...*/};
const _ab & i = anonymousObject;
like image 35
Freddie Chopin Avatar answered Mar 28 '23 22:03

Freddie Chopin