Artur Pyszczuk
Artur Pyszczuk

Reputation: 1930

What is the type of {object}?

Consider following piece of code:

void foo (M&) {}

M m;
foo ({m});

So, the expression {m} is treated as rvalue reference and that is why this code fails to compile.

Is this true that {object} always produces temporary object?

If no, then how to be sure when it happens? If yes, please consider following code:

struct M {};

struct I {
    I (M&) {}
};

struct MM {
    MM (M& p)
    : m (p), i ( {p} )
    {}

    M& m;
    I i;
};

M m;
MM mm {m};

Here there is no problem at all, so what is the difference between {m} from the first example and {p} from the second?

Compiler (GCC 4.8.1) results (first example):

main.cpp:366:13: error: invalid initialization of non-const reference of type ‘M&’ from an rvalue of type ‘<brace-enclosed initializer list>’
     foo ({m});
             ^
main.cpp:359:6: error: in passing argument 1 of ‘void foo(M&)’
 void foo (M&) {}

Upvotes: 8

Views: 199

Answers (2)

Marco A.
Marco A.

Reputation: 43662

Is this true that {object} always produces temporary object?

No (read on).

Both snippets are valid (you should use an up-to-date compiler) and will compile successfully.

The {p} case falls into [over.match.list]/p1

When objects of non-aggregate class type T are list-initialized such that [dcl.init.list] specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:

(1.1) Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

(1.2) If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

Specifically in (1.2), while {m} is a list initialization for the other case.

Upvotes: 0

TartanLlama
TartanLlama

Reputation: 65610

What is the type of {object}?

That depends on the context. In some situations, such as when constructing a type with a std::initializer_list parameter, it'll be of type std::initializer_list<decltype(object)>. In others, it'll be used to initialize members of an aggregate or some other type, in which case the syntactic form {object} doesn't have a type per se.

Is this true that {object} always produces temporary object?

No, and in your case it shouldn't. This was a bug in the C++11 spec relating to the ordering of clauses specifying list initialization. The old wording meant that your call resulted in the construction of a temporary (which is ill-formed due to the non-const reference parameter), but under the new rules, the reference binds to the single initializer list element. Your old compiler implements the old wording, whereas newer compilers implement the fixed behaviour.

Here there is no problem at all, so what is the difference between {m} from the first example and {p} from the second?

The difference is that {m} is used to initialize the M parameter of foo (invalid under the old wording), but {p} initializes the I member. As such, p is taken as the argument to the I constructor, binds to the M& parameter, and since p is an lvalue, everything is fine and dandy.

Upvotes: 2

Related Questions