Це не буде працювати, як зазначено, оскільки list.begin()
має типconst T *
, і ви не можете перейти від постійного об'єкта. Мовні дизайнери, напевно, зробили це для того, щоб дозволити списки ініціалізаторів містити, наприклад, константи рядків, з яких було б недоречно переходити.
Однак, якщо ви потрапили в ситуацію, коли вам відомо, що список ініціалізатора містить вирази rvalue (або ви хочете змусити користувача писати їх), тоді є хитрість, яка змусить його працювати (мене надихнула відповідь Sumant для це, але рішення набагато простіше, ніж це). Вам потрібно, щоб елементи, що зберігаються в списку ініціалізатора, були не T
значеннями, а значеннями, які інкапсулюються T&&
. Тоді навіть якщо ці значення самі const
кваліфіковані, вони все одно можуть отримати модифікуване значення rvalue.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); } // restitute rvalue ref
};
Тепер замість оголошення initializer_list<T>
аргументу ви оголошуєте initializer_list<rref_capture<T> >
аргумент. Ось конкретний приклад, що включає вектор std::unique_ptr<int>
розумних покажчиків, для якого визначена лише семантика переміщення (тому самі ці об'єкти ніколи не можуть зберігатися в списку ініціалізатора); проте список ініціалізаторів, що наведений нижче, складається без проблем.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr; // move only type
std::vector<uptr> data;
public:
uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
uptr_vec(std::initializer_list<rref_capture<uptr> > l)
: data(l.begin(),l.end())
{}
uptr_vec& operator=(const uptr_vec&) = delete;
int operator[] (size_t index) const { return *data[index]; }
};
int main()
{
std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
uptr_vec v { std::move(a), std::move(b), std::move(c) };
std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;
}
На одне питання потрібна відповідь: якщо елементи списку ініціалізаторів мають бути істинними значеннями prvalues (у прикладі це xvalues), чи гарантує мова, що термін служби відповідних тимчасових програм поширюється до точки, де вони використовуються? Чесно кажучи, я не думаю, що відповідний розділ 8.5 стандарту взагалі розглядає це питання. Однак, читаючи 1.9: 10, здається, що відповідний повний вираз у всіх випадках охоплює використання списку ініціалізаторів, тому я думаю, що немає небезпеки звисання посилань rvalue.
initializer_list<T>
, не є const. Мовляв,initializer_list<int>
відноситься доint
об'єктів. Але я думаю, що це дефект - передбачається, що компілятори можуть статично розподіляти список у пам'яті лише для читання.