Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Brace-init-list and assignments

#include <iostream>

struct int_wrapper
{
    int i;

    int_wrapper(int i = 0) : i(i) {}
};

struct A
{
    int_wrapper i;
    int_wrapper j;

    A(int_wrapper i = 0, int_wrapper j = 0) : i(i), j(j) {}
};

int main()
{
    A a;

    a = {3};

    // error: no match for ‘operator=’ (operand types are ‘A’ and ‘int’)
    // a = 3;

    std::cout << a.i.i << std::endl;

    return 0;
}

I know that isn't allowed more than one user-conversion when doing an implicit conversion, but, why with a brace-init-list the double user-conversion works?

like image 324
Peregring-lk Avatar asked Apr 06 '17 02:04

Peregring-lk


People also ask

What is a braced init list?

Remember: using a braced-init-list means "initialize an object". What you are getting is an implicit conversion followed by an object initialization. You're getting "double user-conversion" because that's what you asked for.

What is braced init list in C++?

initializer_list constructorsThe initializer_list Class represents a list of objects of a specified type that can be used in a constructor, and in other contexts. You can construct an initializer_list by using brace initialization: C++ Copy. initializer_list<int> int_list{5, 6, 7}; Important.

How do you initialize a list in C++?

Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.

How does STD initializer list work?

When a compiler sees an initializer list, it automatically converts it into an object of type std::initializer_list. Therefore, if we create a constructor that takes a std::initializer_list parameter, we can create objects using the initializer list as an input.


1 Answers

Remember: using a braced-init-list means "initialize an object". What you are getting is an implicit conversion followed by an object initialization. You're getting "double user-conversion" because that's what you asked for.

When you do a = <something>;, what this does is the effective equivalent of a.operator=(<something>).

When that something is a braced-init-list, this means that it will do a.operator=({3}). That will pick an operator= overload, based on initializing their first parameter from a braced-init-list. The overload that will be called will be the one that takes a type which can be initialized by the values in the braced-init-list.

There are two overloads of that operator. Namely, the copy-assignment and move-assignment. Since this braced-init-list initializes a prvalue, the preferred function to call will be the move-assignment operator (not that this matters, since it all leads to the same thing). The parameter of the move-assignment is A&&, so the braced-init-list will attempt to initialize an A. And it will do so via the rules of copy-list-initialization.

Having selected the operator= function to call, we now initialize A. Since the constructor of A is not explicit, copy-list-initialization is free to call it. Since that constructor has 2 default parameters, it can be called with only a single parameter. And the type of the first parameter in A's constructor, int_wrapper, is implicitly convertible from the type of the first value in the braced-init-list, int.

So, you get an implicit conversion to int_wrapper, which is used by copy-list-initialization to initialize a prvalue temporary object, which is used to assign to an existing object of type A via move-assignment.

By contrast, a.operator=(3) attempts to implicitly convert from 3 to A directly. Which of course requires two conversion steps and therefore is not allowed.

Just remember that braced-init-lists mean "initialize an object".

like image 137
Nicol Bolas Avatar answered Sep 22 '22 00:09

Nicol Bolas