Je suppose que non, mais je voudrais confirmer. Y a-t-il une utilisation pour const Foo&&
, où Foo
est un type de classe?
Je suppose que non, mais je voudrais confirmer. Y a-t-il une utilisation pour const Foo&&
, où Foo
est un type de classe?
Réponses:
Ils sont parfois utiles. Le projet C ++ 0x lui-même les utilise à quelques endroits, par exemple:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Les deux surcharges ci-dessus garantissent que l'autre ref(T&)
cref(const T&)
fonctions et ne se lient pas aux valeurs r (ce qui serait autrement possible).
Mise à jour
Je viens de vérifier la norme officielle N3290 , qui n'est malheureusement pas disponible publiquement, et elle contient en 20.8 les objets Function [function.objects] / p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Ensuite, j'ai vérifié le brouillon post-C ++ 11 le plus récent, qui est disponible publiquement, N3485 , et dans 20.8 Function objects [function.objects] / p2, il dit toujours:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
const T&&
Est-ce que d' autres endroits sont utilisés?
const T&&
empêche quelqu'un d'utiliser bêtement des arguments de modèle explicites du formulaire ref<const A&>(...)
. Ce n'est pas un argument très fort, mais le coût du const T&&
dépassement T&&
est assez minime.
La sémantique pour obtenir une référence const rvalue (et non pour =delete
) consiste à dire:
Le cas d'utilisation suivant aurait pu être à mon humble avis un bon cas d' utilisation de la référence rvalue à const , bien que le langage ait décidé de ne pas adopter cette approche (voir l'article original de SO ).
Il serait généralement conseillé d'utiliser make_unique
et make_shared
, mais les deux unique_ptr
et shared_ptr
peuvent être construits à partir d'un pointeur brut. Les deux constructeurs obtiennent le pointeur par valeur et le copient. Les deux permettent (c'est-à-dire au sens de: ne pas empêcher ) une utilisation continue du pointeur d'origine qui leur est passé dans le constructeur.
Le code suivant se compile et les résultats avec double free :
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Les deux unique_ptr
et shared_ptr
pourraient empêcher ce qui précède si leurs constructeurs concernés s'attendaient à obtenir le pointeur brut comme une valeur constante , par exemple pour unique_ptr
:
unique_ptr(T* const&& p) : ptr{p} {}
Dans ce cas, le double code gratuit ci-dessus ne se compilerait pas, mais ce qui suit:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Notez que cela ptr
pourrait encore être utilisé après avoir été déplacé, donc le bogue potentiel n'est pas totalement parti. Mais si l'utilisateur est obligé d'appeler std::move
un tel bogue tomberait dans la règle courante de: ne pas utiliser une ressource qui a été déplacée.
On peut se demander: OK, mais pourquoi T*
const&& p
?
La raison est simple, pour permettre la création d'un unique_ptr
pointeur depuis const . Rappelez-vous que la référence const rvalue est plus générique qu'une simple référence rvalue car elle accepte à la fois const
et non-const
. Nous pouvons donc permettre ce qui suit:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
cela n'irait pas si nous attendions juste une référence rvalue (erreur de compilation: impossible de lier const rvalue à rvalue ).
Quoi qu'il en soit, il est trop tard pour proposer une telle chose. Mais cette idée présente une utilisation raisonnable d'une référence rvalue à const .
Ils sont autorisés et même les fonctions classées en fonction const
, mais comme vous ne pouvez pas vous déplacer d'un objet const référencé par const Foo&&
, elles ne sont pas utiles.
const T&, T&, const T&&, T&&
Outre std :: ref , la bibliothèque standard utilise également la référence const rvalue dans std :: as_const dans le même but.
template <class T>
void as_const(const T&&) = delete;
Il est également utilisé comme valeur de retour dans std :: facultatif lors de l'obtention de la valeur encapsulée:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
Ainsi que dans std :: get :
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
Ceci est vraisemblablement pour conserver la catégorie de valeur ainsi que la constance du wrapper lors de l'accès à la valeur encapsulée.
Cela fait une différence si les fonctions qualifiées par const rvalue peuvent être appelées sur l'objet encapsulé. Cela dit, je ne connais aucune utilisation des fonctions qualifiées const rvalue ref.
Je ne peux pas penser à une situation où cela serait utile directement, mais cela pourrait être utilisé indirectement:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
Le T en g est T const, donc f x est un T const &&.
Il est probable que cela entraîne une erreur de comile dans f (quand il essaie de déplacer ou d'utiliser l'objet), mais f pourrait prendre un rvalue-ref afin qu'il ne puisse pas être appelé sur lvalues, sans modifier la rvalue (comme dans le trop simple exemple ci-dessus).
const&&
c'est très important, bien qu'il ne dise pas pourquoi: youtube.com/watch?v=JhgWFYfdIho#t=54m20s