Our last C++ quiz was a big success. A lot of C++ folks participated and there was one lucky winner. That’s why we decided to make it a tradition and keep the quiz juices flowing and present you with yet another C++ fun quiz!
Thanks to Anders Schau Knatten from CppQuiz for creating this quiz!
I hope you enjoy it.
The C++ quiz!
#include <initializer_list>
#include <iostream>
class C {
public:
C() = default;
C(const C&) { std::cout << 1; }
};
void f(std::initializer_list<C> i) {}
int main() {
C c;
std::initializer_list<C> i{c};
f(i);
f(i);
}
Hint:
The result would be the same if we called f(i) only once.
The answer:
The program is guaranteed to output 1
Explanation:
The class C prints 1 when it’s copied. But when is it copied?
[dcl.init.list]§11.6.4¶5 in the standard explains construction of initializer_list:
An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation generated and materialized (7.4) a prvalue of type “array of N const E”, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array.
So when we construct the initializer list i, we can imagine a temporary array of one C being created, where the element is copy-initialized. Since c is an lvalue, a copy is made (not for instance a move), and 1 is printed.
Then, f is called twice, taking the freshly created initializer_list by value. So a copy of the initializer_list is made for each call. Does that make a copy of the elements in the initializer list, as it would when taking for instance a vector or a list by value? No. As we saw at the end of the quote above:
the std::initializer_list<E> object is constructed to refer to that array.
So the initializer_list only refers to that array, it doesn’t have a copy of it. A note in [initializer_list.syn]§21.9.1¶1 spells it out verbatim:
Copying an initializer list does not copy the underlying elements.
The key takeaway is: If you write an initializer list constructor for your class, never move the elements out of the initializer list. Even if you took it by value, you were not passed an exclusive copy of the elements, which might still be used by others.