Cela ne fonctionne pas comme indiqué, car list.begin()
type const T *
, et il n'y a aucun moyen que vous pouvez déplacer à partir d'un objet constant. La langue concepteurs probablement fait que c'est dans le but de permettre à l'initialiseur listes contiennent par exemple des constantes de chaîne, à partir de laquelle il serait inapproprié de se déplacer.
Toutefois, si vous êtes dans une situation où vous savez que l'initialiseur liste contient rvalue expressions (ou si vous voulez forcer l'utilisateur à écrire ces) puis il y a un truc qui va faire fonctionner (j'ai été inspiré par la réponse par Sumant pour cela, mais la solution est plus simple que ça). Vous avez besoin des éléments stockés dans les initialiser la liste pour ne pas être T
valeurs, mais les valeurs qui encapsulent T&&
. Alors, même si ces valeurs elles-mêmes sont const
qualifiés, ils peuvent toujours récupérer modifiable 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
};
Maintenant, au lieu de déclarer un initializer_list<T>
argument, vous déclarez uninitializer_list<rref_capture<T> >
argument. Voici un exemple concret, impliquant un vecteur d' std::unique_ptr<int>
des pointeurs intelligents, pour qui seule la sémantique de déplacement est défini (de sorte que ces objets eux-mêmes ne peuvent jamais être stockés dans une liste d'initialiseur); cependant, l'initialiseur de la liste ci-dessous compile sans problème.
#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;
}
Une question nécessite une réponse: si les éléments de la liste d'initialiseur doit être vrai prvalues (dans l'exemple, ils sont xvalues), la langue s'assurer que la durée de vie de l'correspondant temporaires s'étend jusqu'au point où ils sont utilisés? Franchement, je ne pense pas que la section correspondante de l'article 8.5 de la norme traite de cette question. Cependant, la lecture de 1.9:10, il semblerait que la pleine expression dans tous les cas, englobe l'utilisation de la liste d'initialiseur, donc je pense qu'il n'y a pas de danger, en balançant les références rvalue.