Why does braced-init-list behave differently in a function call versus a constructor invocation?
Asked Answered
I

2

5

Compiling the following code with clang 3.5.0 and gcc 4.9.1 yields an error at the last statement.

#include <iostream>

struct Foo { Foo(int x, int y) { std::cout << "Foo(int = " << x << ", int = " << y << ")" << std::endl; } };

void bar(int x, int y) { std::cout << "bar(int = " << x << ", int = " << y << ")" << std::endl; }

int main()
{
   Foo({}, {});   // Foo(int = 0, int = 0)
   Foo({1}, {2}); // Foo(int = 1, int = 2)
   Foo({1, 2});   // Foo(int = 1, int = 2)

   bar({}, {});   // bar(int = 0, int = 0)
   bar({1}, {2}); // bar(int = 1, int = 2)
   bar({1, 2});   // error: no matching function for call to 'bar'  <<< Why? <<<
}

Why is Foo({1, 2}) okay while bar({1, 2}) is not?

Particularly, it would be great to learn about the rationale.

Ipsambul answered 7/11, 2014 at 16:55 Comment(3)
My guess is, Foo({1,2}) creates a temporary Foo object and calls the copy ctor.Leith
The comment by @Leith is correct - with {1, 2} you can create a temporary Foo object, but only when Foo is expected. You cannot actually pass multiple parameters to function with that.Lipps
Huh, funny... interestingly vec.insert(vec.back(), {...}) works just fine, contrary to your code (calls the std::initializer_list version). I just wrote that a few mins ago hoping the C++ committee was smart enough to think of that when they implemented initializer lists, so this will "just work", and it does indeed. Which is, truly, awesome.Versicle
L
6

Foo({1,2}) creates a temporary Foo object and calls the copy constructor.

See this modified example with copy constructor delete: http://coliru.stacked-crooked.com/a/6cb80746a8479799

It errors with:

main.cpp:6:5: note: candidate constructor has been explicitly deleted
    Foo(const Foo& f) = delete;
Leith answered 7/11, 2014 at 16:59 Comment(5)
But wasn't a copy constructor implicitly generated in the original example ? Why delete it and cause another reason not to compile ? This does not exaplain why bar cannot be called; and it's all about the arguments of bar, see this way of fixing compilationFart
@NorahAttkins You missed the point. The point was, the initializer list isn't passing the 2 ints to the constructor (which the OP assumed, and wondered why it wouldn't work in a function call that also takes 2 ints), it's generating a temporary object and calling the copy constructor. Deleting it showed that the copy constructor was being called.Leith
No it didn't; Watch this example If I remove the call to Foo({1, 2}) the program still fails to compile when calling bar without ever mentioning anything about deleted copy constructors. The call to bar never created a temporary Foo objectFart
@NorahAttkins Re-read what I said, OP's reasonable assumption was that the initializer list was compatible with a signature taking two ints, when in reality in the case of the Foo object it was creating a temporary, which meant that he shouldn't expect the call to bar to succeed. Why the call to bar fails is a different matter.Leith
Yes, you are right; silly of me. Thanks for schooling me on initializer_list types .... em I should say "no types"Fart
F
1

The line

bar({1, 2});

actually passes in the bar function, a temporary object of type

<brace-enclosed initializer list> // it's made clear in the comments that brace initializers have no type

and there is no way to convert this temporary object to the type of the first argument which is an int. Thus the error

cannot convert <brace-enclosed initializer list> to int for argument 1 to

void bar(int, int)
Fart answered 7/11, 2014 at 17:2 Comment(5)
No, that is not correct. A braced-init-list does not have a type.Mannerly
@NorahAttkins A braced initializer has no typeLeith
Yes. What you are showing results from a special (idiotic) rule introduced precisely for this case.Mannerly
@Leith That's a bit confusing. Doesn't the program still fail when calling bar due to type mismatch ? (I'll watch the video soon) To my understanding, no type resembles "lambdas have no type" meaning they have a type which we don't knowFart
@NorahAttkins Except lambdas have a type, but it's anonymous.Leith

© 2022 - 2024 — McMap. All rights reserved.