Avantages de l'utilisation de l'avant
De transfert parfait, std::forward
est utilisé pour convertir le nommé références rvalue t1
et t2
anonymes références rvalue. Quel est le but de le faire? Comment cela affecterait-il la fonction appelée inner
si nous laissons t1
& t2
comme lvalues?
template <typename T1, typename T2>
void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
Vous devez vous connecter pour publier un commentaire.
Vous devez comprendre la transmission de problème. Vous pouvez lire l'intégralité du problème en détail, mais je vais résumer.
Fondamentalement, compte tenu de l'expression
E(a, b, ... , c)
, nous voulons l'expressionf(a, b, ... , c)
équivalent. En C++03, c'est impossible. Il y a beaucoup de tentatives, mais ils ont tous échoué dans certains égard.Le plus simple est d'utiliser une lvalue-référence:
Mais cela ne parvient pas à gérer des valeurs temporaires:
f(1, 2, 3);
, que ceux qui ne peuvent pas être lié à une lvalue de référence.La prochaine tentative peut-être:
Qui résout le problème ci-dessus, mais flips flops. Désormais, elle ne parvient pas à permettre
E
non-const arguments:La troisième tentative accepte const-références, mais alors
const_cast
's laconst
loin:Ce accepte toutes les valeurs, peut passer sur toutes les valeurs, mais aussi, potentiellement, conduit à un comportement indéfini:
Une solution finale s'occupe de tout correctement...au prix d'être impossible à maintenir. Vous fournir des surcharges de
f
, avec tous combinaisons de const et non const:N arguments besoin de 2N combinaisons, un cauchemar. Nous aimerions faire cela automatiquement.
(C'est effectivement ce que nous obtenons le compilateur de faire pour nous en C++11.)
En C++11, on a une chance de résoudre ce problème. Une solution modifie modèle de règles de déduction sur les types existants, mais cela risque de casse une grande quantité de code. Nous devons donc trouver un autre moyen.
La solution est d'utiliser à la place de la nouvelle rvalue-références; nous pouvons introduire de nouvelles règles lorsque le retrait rvalue-types de référence et la création de résultat souhaité. Après tout, nous ne pouvons pas le code de saut de maintenant.
Si une référence à une référence (note de référence est un terme général signifiant à la fois
T&
etT&&
), nous utilisons la règle suivante pour déterminer le type résultant:Ou sous forme de tableaux:
Prochaine, avec l'argument de modèle déduction: si un argument est une lvalue, nous offre l'argument de modèle avec une lvalue référence à A. dans le cas Contraire, nous en déduisons que normalement. Cela donne soi-disant des références universelles (le terme la redirection de référence est maintenant officiel).
Pourquoi est-ce utile? Parce combiné nous maintenir la capacité de garder la trace de la valeur de la catégorie de type: si c'était une lvalue, nous avons une lvalue paramètre de référence, sinon nous avons une rvalue paramètre de référence.
Dans le code:
La dernière chose est de "suivre" la valeur de la catégorie de la variable. Gardez à l'esprit, une fois à l'intérieur de la fonction, le paramètre peut être passé comme une lvalue à rien:
Ce n'est pas bon. E besoin pour obtenir le même type de valeur de la catégorie que nous avons eu! La solution est: est-ce
À quoi ça sert? Considérons que nous sommes à l'intérieur du
deduce
fonction, et nous avons passé une lvalue. Cela signifieT
est unA&
, et donc le type de cible pour la statique de la fonte estA& &&
, ou tout simplementA&
. Depuisx
est déjà unA&
, nous ne faisons rien et sont à gauche avec une lvalue de référence.Quand nous avons passé une rvalue,
T
estA
, de sorte que le type de cible pour la statique de la fonte estA&&
. La distribution des résultats dans une rvalue expression, qui ne peut plus être transmis à une lvalue de référence. Nous avons maintenu la valeur du paramètre.Mettre ces ensemble nous donne la "perfect forwarding":
Quand
f
reçoit une lvalue,E
obtient une lvalue. Lorsquef
reçoit une rvalue,E
obtient une rvalue. Parfait.Et bien sûr, nous voulons nous débarrasser de ce qui est laid.
static_cast<T&&>
est énigmatique et étrange de se rappeler; nous allons plutôt faire une fonction utilitaire appeléforward
, qui fait la même chose:f
une fonction, et non pas une expression?f
lui-même pourrait être n'importe quoi "callable"; une fonction de pointeur de fonction, ou de la fonction de l'objet.const int i
seront acceptées:A
est déduit àconst int
. Les échecs sont pour les rvalues littéraux. Notez également que, pour l'appel àdeduced(1)
, x estint&&
, pasint
(transfert parfait jamais fait une copie, comme le ferait six
serait d'une valeur de paramètre). SimplementT
estint
. La raison quex
évalue à une lvalue dans le transitaire est parce que nommé références rvalue devenir lvalue expressions.x
évalue à un lvalue dans le transitaire etstatic_cast<T&&>(x)
évalue à un value dans le transitaire. Lvalues et xvalues sont appelés les glvalues. Prvalues (littéraux, etc...) et xvalues sont appelés les rvalues.A
être déduites àconst int
dans la première partie, n'était tout simplement pas à l'esprit.) Je vais vous avouer que je n'ai pas encore lu la nouvelle taxonomie ou de règles de déduction. :S (fait Juste, pas trop mal.) Est-ce correct?forward
oumove
ici? Ou est-ce juste une différence sémantique?std::forward
peuvent alors soit. Utilisationstd::move
quand vous savez que vous n'avez plus besoin de la valeur et vous souhaitez le déplacer ailleurs, l'utilisationstd::forward
de le faire selon les valeurs transmises à votre modèle de fonction.template <typename T> void check(const T&)
?Je pense avoir conceptuel de l'application du code des std::forward peut ajouter à la discussion. C'est une diapositive à partir de Scott Meyers parler Effective C++11/14 Sampler
Fonction
move
dans le code eststd::move
. Il y a un (de travail) mise en œuvre pour plus tôt dans ce discours. J'ai trouvé mise en œuvre effective de std::forward dans libstdc++, dans le fichier à déplacer.h, mais il n'est pas instructif.À partir d'un point de vue utilisateur, la signification de cela est que
std::forward
est une condition de fonte pour une rvalue. Il peut être utile si je suis en train d'écrire une fonction qui attend une lvalue ou rvalue dans un paramètre et veut passer à une autre fonction comme une rvalue que s'il est transmis sous la forme d'une rvalue. Si je n'ai pas envelopper le paramètre dans std::forward, il serait toujours passé comme une normale de référence.Assez sûr, il imprime
Le code est basé sur un exemple déjà mentionné parler. Diapositive 10, à environ 15:00 depuis le début.
Si vous utilisez un nom de référence rvalue dans une expression, il est en fait une lvalue (parce que vous vous référez à l'objet par son nom). Considérons l'exemple suivant:
Maintenant, si nous appelons
outer
comme cenous aimerions 17 et 29 à être transmis à #2, car les 17 et 29 sont des entiers littéraux et en tant que tel rvalues. Mais depuis
t1
ett2
dans l'expressioninner(t1,t2);
sont lvalues, vous seriez en invoquant #1 à la place de #2. C'est pourquoi nous avons besoin de tourner les références dans les références sans nom avecstd::forward
. Donc,t1
dansouter
est toujours une lvalue expression tout enforward<T1>(t1)
peut être une rvalue expression en fonction deT1
. Ce dernier est seulement une lvalue expression siT1
est une lvalue de référence. EtT1
est uniquement déduite d'être une lvalue de référence dans le cas où le premier argument à l'extérieur est une lvalue expression.Si, après l'instanciation,
T1
est de typechar
, etT2
est d'une classe, vous voulez passert1
par copie ett2
parconst
de référence. Eh bien, à moins queinner()
les prend par la non-const
de référence, qui est, dans le cas où vous voulez le faire, trop.Essayer d'écrire un ensemble de
outer()
fonctions qui implémentent cette sans références rvalue, en déduire la bonne façon de passer les arguments à partir deinner()
's type. Je pense que vous aurez besoin de quelque chose 2^2 d'entre eux, assez bien membré, un modèle de méta trucs pour en déduire les arguments, et beaucoup de temps pour obtenir ce droit pour tous les cas.Et puis quelqu'un vient avec une
inner()
qui prend comme arguments par pointeur. Je pense que le fait maintenant 3^2. (Ou 4^2. L'enfer, je ne peux pas être pris la peine d'essayer de réfléchir siconst
pointeur de faire la différence.)Et puis imaginez que vous voulez faire cela pour une durée de cinq paramètres. Ou sept.
Maintenant vous savez pourquoi certains esprits lumineux est venu avec "le transfert parfait": Il fait le compilateur de faire tout cela pour vous.
Un point qui n'a pas été clairs sur le fait que
static_cast<T&&>
poignéesconst T&
correctement aussi.Programme:
Produit:
Noter que 'f' est une fonction de modèle. Si c'est simplement défini comme " void f(int&& a)' cela ne fonctionne pas.
Il peut être intéressant de souligner que l'avant doit être utilisé en tandem avec une méthode extérieure de l'envoi/de référence universel. À l'aide de l'avant par lui-même que les énoncés suivants est autorisé, mais n'est d'aucune utilité autre que de causer de la confusion. Comité de la norme souhaiterez peut-être désactiver cette flexibilité autrement, pourquoi ne pas simplement utiliser static_cast à la place?
À mon avis, de se déplacer et sont des modèles de conception qui sont naturelles des résultats après la r-valeur de référence de type est introduite. Nous ne devrions pas le nom d'une méthode en supposant qu'il est correctement utilisée, à moins que la mauvaise utilisation est interdite.