Humam Helfawi Humam Helfawi - 1 month ago 7
C++ Question

Why I can not return initializer list from lambda

Why this could is not valid?

auto foo=[](){
return {1,2};
};


However, this is valid since the
initializer list
is used just to initialize a
vector
not to return itself:

auto foo=[]()->std::vector<int>{
return {1,2};
};


Why I can not return
initializer list
? It could be useful. For example, a lambda that can be used to initialize a
vector
or a
list
or ... with some default values for something.

Answer

Lambda return type deduction uses the auto rules, which normally would have deduced std::initializer_list just fine. However, the language designers banned deduction from a braced initializer list in a return statement ([dcl.spec.auto]/7):

If the deduction is for a return statement and the initializer is a braced-init-list ([dcl.init.list]), the program is ill-formed.

The reason for this is that std::initializer_list has reference semantics ([dcl.init.list]/6).
[]() -> std::initializer_list<int> { return {1, 2}; } is every bit as bad as
[]() -> const int & { return 1; }. The lifetime of the backing array of the initializer_list object ends when the lambda returns, and you are left with a dangling pointer (or two).

Demo:

#include <vector>

struct Noisy {
    Noisy()  { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    Noisy(const Noisy&) { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    ~Noisy() { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
};

int main()
{
    auto foo = []() -> std::initializer_list<Noisy> { return {Noisy{}, Noisy{}}; };
    std::vector<Noisy> bar{foo()};
}

Output:

Noisy::Noisy()
Noisy::Noisy()
Noisy::~Noisy()
Noisy::~Noisy()
Noisy::Noisy(const Noisy&)
Noisy::Noisy(const Noisy&)
Noisy::~Noisy()
Noisy::~Noisy()

Note how the copy constructors are called after all the Noisy objects created so far have been destroyed already.