Question

J'ai un C ++ structure de données qui est nécessaire « scratchpad » pour d'autres calculs. Ce n'est pas à long terme, et il est peu utilisé donc pas les performances critiques. Cependant, il comprend un générateur de nombres aléatoires entre autres champs de suivi actualisables, et alors que la valeur réelle du générateur est pas important, il important que la valeur est mise à jour plutôt que copiée et réutilisée. Cela signifie que, en général, les objets de cette classe sont passés par référence.

Si une instance est nécessaire qu'une seule fois, l'approche la plus naturelle est de les construire whereever nécessaire (peut-être en utilisant une méthode de fabrication ou d'un constructeur), et en faisant passer le scratchpad à la méthode consommation. Les signatures de méthode de consommateurs utilisent passent par référence, car ils ne savent pas que ce soit la seule utilisation, mais les méthodes d'usine et les constructeurs reviennent en valeur -. et vous ne pouvez pas passer sans nom par référence temporaires

Y at-il un moyen d'éviter le colmatage du code avec des variables temporaires désagréables? Je voudrais éviter des choses comme suit:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);

Je pourrais faire le scratchpad intrinsèquement mutable et juste étiqueter tous const & paramètres, mais cela ne me paraît pas le plus pratique car il est trompeur, et je ne peux pas le faire pour les classes que je ne contrôle pas complètement. Passant par référence rvalue, il faudrait ajouter à tous les consommateurs les surcharges de scratchpad, quel genre de défaites le but -. Ayant un code clair et concis

Compte tenu du fait que la performance n'est pas critique (mais la taille du code et la lisibilité sont), Quelle est la meilleure pratique approche de passer dans un tel scratchpad? en C ++ propose 0x est OK si < em> requis mais de préférence en C ++ 03 caractéristiques uniquement devraient suffire.

Edit: Pour être clair, en utilisant un encombrement temporaire est faisable, il est juste dommage dans le code que je voudrais éviter. Si vous ne donnez jamais le nom temporaire a, il est clairement utilisé qu'une seule fois, et les moins de lignes de code à lire, mieux. En outre, dans les initialiseurs de constructeurs, il est impossible de déclarer temporaires.

Était-ce utile?

La solution

Bien qu'il soit correct de passer rvalues ??aux fonctions d'accepter des références non-const, il est correct d'appeler des fonctions membres sur rvalues, mais la fonction de membre ne sait pas comment il a été appelé. Si vous retournez une référence à l'objet courant, vous pouvez convertir rvalues ??à lvalues:

class scratchpad_t
{
    // ...

public:

    scratchpad_t& self()
    {
        return *this;
    }
};

void foo(scratchpad_t& r)
{
}

int main()
{
    foo(scratchpad_t().self());
}

Notez comment l'appel à self() donne une expression lvalue même si scratchpad_t est un rvalue.

  

S'il vous plaît me corriger si je me trompe, mais les paramètres de référence rvalue n'acceptent pas les références lvalue afin de les utiliser, il faudrait ajouter à tous les consommateurs les surcharges de scratchpad, qui est également regrettable.

Eh bien, vous pouvez utiliser des modèles ...

template <typename Scratch> void foo(Scratch&& scratchpad)
{
    // ...
}

Si vous appelez foo avec un paramètre rvalue, Scratch sera déduit à scratchpad_t, et donc Scratch&& sera scratchpad_t&&.

Et si vous appelez foo avec un paramètre lvalue, Scratch sera déduit à scratchpad_t&, et en raison des règles effondrement de référence, Scratch&& sera également scratchpad_t&.

Notez que le scratchpad paramètre formel est un nom et donc une lvalue, peu importe si son type est une référence lvalue ou une référence rvalue. Si vous voulez passer scratchpad à d'autres fonctions, vous n'avez pas besoin du tour de modèle pour ces fonctions plus, il suffit d'utiliser un paramètre de référence lvalue.

Par ailleurs, vous rendre compte que le module provisoire impliqué dans xyz.initialize_computation(scratchpad_t(1, 2, 3)); sera détruite dès que initialize_computation est fait, non? Le stockage de la référence dans l'objet xyz pour l'utilisateur plus tard serait une très mauvaise idée.

  

self() n'a pas besoin d'être une méthode membre, il peut être une fonction templated

Oui, qui est également possible, bien que je le renommer pour faire l'intention plus claire:

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

Autres conseils

Le problème est juste que ceci:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);

est laid? Si oui, alors pourquoi ne pas changer à ceci:

auto useless_temp = factory(rng_parm);

Personnellement, je préfère voir const_cast que mutable. Quand je vois mutable, je suppose que quelqu'un de faire const-ness logique, et ne pense pas beaucoup. const_cast soulève cependant des drapeaux rouges, code comme cela devrait.

Une option serait d'utiliser quelque chose comme shared_ptr (auto_ptr fonctionnerait aussi en fonction de ce que factory fait) et le transmettre par valeur, ce qui évite le coût de la copie et maintient une seule instance, mais peut être passé de votre usine méthode.

Si vous allouez l'objet dans le tas, vous pourriez être en mesure de convertir le code à quelque chose comme:

std::auto_ptr<scratch_t> create_scratch();

foo( *create_scratch() );

L'usine crée et retourne un auto_ptr au lieu d'un objet dans la pile. Le retour temporaire auto_ptr prendra possession de l'objet, mais vous êtes autorisé à appeler des méthodes non-const sur une base temporaire et vous pouvez déréférencer le pointeur pour obtenir une véritable référence. Au point suivant de séquence le pointeur intelligent sera détruit et la mémoire libérée. Si vous devez passer la même scratch_t à des fonctions différentes dans une ligne que vous pouvez capturer simplement le pointeur intelligent:

std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );

Il peut être remplacé par std::unique_ptr dans la norme à venir.

Je marqué la réponse de FredOverflow comme la réponse pour sa suggestion d'utiliser une méthode pour revenir simplement une référence non-const; cela fonctionne en C ++ 03. Cette solution nécessite une méthode de membre par scratchpad comme le type, mais en C ++ 0x on peut aussi écrire cette méthode plus généralement pour tout type:

template <typename T> T & temp(T && temporary_value) {return temporary_value;}

Cette fonction références lvalue simplement vers l'avant normale, et convertis rvalue références dans les références lvalue. Bien sûr, cette opération renvoie une valeur modifiable dont le résultat est ignoré -. Qui se trouve être exactement ce que je veux, mais il peut sembler étrange dans certains contextes

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top