This question already has an answer here:
- initializer_list and move semantics 6 answers
Still on my quest to fully understand uses and caveats of std::initializer_list, I have found yet another surprising behavior. I understand that initializer lists are a sort of immutable container, in my mind I'd think of it as a list of arguments passed as const references, which can lead to excessive copies in some cases (my understanding is that they contain some sort of sequence of locally valid pointers). However, I found that constructing an initializer list produces copies of its contents. An example:
#include <initializer_list>
#include <iostream>
#include <vector>
struct A
{
A()
{
std::cout << "Creating A" << std::endl;
}
~A()
{
std::cout << "Deleting A" << std::endl;
}
A(const A &other)
{
std::cout << "Copying A" << std::endl;
}
A(A &&other)
{
std::cout << "Moving A" << std::endl;
}
A &operator=(const A &other)
{
std::cout << "Copy-assigning A" << std::endl;
return *this;
}
A &operator=(A &&other)
{
std::cout << "Move-assigning A" << std::endl;
return *this;
}
};
int main(int, char **)
{
A a;
std::cout << "Creating initializer list 1." << std::endl;
std::initializer_list<A> il1 { a };
std::cout << "Created initializer list 1." << std::endl;
std::vector<A> v {A(), A(), A()};
std::cout << "Creating initializer list 2." << std::endl;
std::initializer_list<std::vector<A>> il2 { v };
std::cout << "Created initializer list 2." << std::endl;
return 0;
}
And the output (with GCC 5.4 and Clang 3.8):
Creating A
Creating initializer list 1.
Copying A
Created initializer list 1.
Creating A
Creating A
Creating A
Copying A
Copying A
Copying A
Deleting A
Deleting A
Deleting A
Creating initializer list 2.
Copying A
Copying A
Copying A
Created initializer list 2.
Deleting A
Deleting A
Deleting A
Deleting A
Deleting A
Deleting A
Deleting A
Deleting A
The relevant parts are those between Creating... and Created..., where it can be seen that the initializer list produces copies of its arguments. I don't understand the point of this; making copies on construction but not allowing to move later looks like the worst of both worlds to me. It's nice that copying an initializer list does not causes copies, but if it does on construction I don't see how it can be used for heavy objects.
I haven't been able to find a reliable source stating whether creating an initializer list should cause copies or not, but from most comments it seems that the general belief is that they don't.
I've also realized that passing rvalues to the initializer list does not cause copies. That's great and all, but since I won't be able to move them out of the list later anyway I don't see why that should be a requirement.
Aucun commentaire:
Enregistrer un commentaire