47 votes

Pourquoi n'y a-t-il pas de méthodes swap () générées par le compilateur en C ++0x?

Compilateurs C++ générer automatiquement copier les constructeurs et la copie des opérateurs d'affectation. Pourquoi ne pas swap trop?

Ces jours-ci la méthode préférée pour la mise en œuvre de la copie-opérateur d'affectation est la copie-et-swap idiome:

T& operator=(const T& other)
{
    T copy(other);
    swap(copy);
    return *this;
}

(en ignorant le copier-élision-forme conviviale qui utilise le passage par valeur).

Ce langage a l'avantage d'être transactionnelle dans le visage des exceptions (en supposant que l' swap mise en œuvre n'a pas les jeter). En revanche, la valeur par défaut généré par le compilateur copier-opérateur d'affectation de manière récursive ne copie-affectation sur toutes les classes de base et des données des membres, et qui n'ont pas la même exception de garanties de sécurité.

Pendant ce temps, la mise en œuvre de swap méthodes à la main est fastidieux et source d'erreurs:

  1. Pour s'assurer que swap ne les jetez pas, il doit être mis en œuvre pour tous les non-POD membres dans la classe et dans les classes de base, dans leur non-POD membres, etc.
  2. Si un responsable ajoute un nouveau membre de données à une classe, le responsable doit veiller à modifier cette classe swap méthode. À défaut de le faire peut introduire des bogues subtils. Aussi, depuis swap est une méthode ordinaire, les compilateurs (au moins aucun que je connais) n'émettent pas de mises en garde si l' swap mise en œuvre est incomplète.

Ne serait-il pas mieux si le compilateur a généré swap méthodes automatiquement? Ensuite, l'implicite de copie d'attribution de la mise en œuvre pourrait en tirer profit.

La réponse évidente est probablement: la copie-et-swap idiome n'existait pas quand C++ a été développé, et à faire maintenant pourrait casser le code existant.

Encore, peut-être que les gens pourraient choisir de laisser le compilateur de générer swap en utilisant la même syntaxe que le C++0x utilise pour contrôler d'autres fonctions implicites:

void swap() = default;

et puis il pourrait y avoir des règles:

  1. Si il est généré par le compilateur swap méthode implicite de copier-opérateur d'affectation peuvent être mises en œuvre à l'aide de la copie et de l'échange.
  2. Si il n'est pas généré par le compilateur swap méthode implicite de copier-opérateur d'affectation serait mis en œuvre comme avant (en invoquant copier-transfert sur toutes les classes de base et de tous les membres).

Personne ne sait si cette (folle?) les choses ont été suggérées pour le C++ comité des normes, et, dans l'affirmative, quelles sont les opinions des membres du comité avaient?

24voto

GManNickG Points 155079

C'est en plus de Terry réponse.

La raison, nous avons eu à faire swap des fonctions en C++ avant 0x est parce que le général de libre-fonction std::swap a été moins efficace (et moins polyvalent) qu'il pourrait l'être. Il a fait une copie d'un paramètre, puis avait deux ré-affectations, puis relâché le essentiellement gaspillage de copie. Faire une copie d'un poids lourd de la classe est une perte de temps, quand nous, les programmeurs connaissent tous nous avons vraiment besoin de faire est de permuter les pointeurs internes et autres joyeusetés.

Cependant, rvalue-références soulager cette complètement. Dans C++0x swap est mis en œuvre comme:

template <typename T>
void swap(T& x, T& y)
{
    T temp(std::move(x));
    x = std::move(y);
    y = std::move(temp);
}

Cela fait beaucoup plus de sens. Au lieu de copier des données, nous ne faisons que déplacer les données. Cela permet même à des non-copiable types, comme les ruisseaux, pour être échangé. Le projet de C++0x norme précise que, pour les types à être échangé avec std::swap, ils doivent être rvalue constructable, et rvalue assignables (évidemment).

Cette version de l' swap essentiellement en faire ce que tout personnalisées écrites fonction d'échange ferait. Considérons une classe que nous aurions normalement écrire swap pour (comme cette "bête" vecteur):

struct dumb_vector
{
    int* pi; // lots of allocated ints

    // constructors, copy-constructors, move-constructors
    // copy-assignment, move-assignment
};

Auparavant, swap serait de faire une copie redondante de l'ensemble de nos données, avant de le jeter plus tard. Notre coutume swap fonction serait juste changer le pointeur, mais peut être maladroit d'utiliser dans certains cas. Dans C++0x, le déplacement d'atteindre le même résultat final. Appelant std::swap deviendra:

dumb_vector temp(std::move(x));
x = std::move(y);
y = std::move(temp);

Ce qui se traduit par:

dumb_vector temp;
temp.pi = x.pi; x.pi = 0; // temp(std::move(x));
x.pi = y.pi; y.pi = 0; // x = std::move(y);
y.pi = temp.pi; temp.pi = 0; // y = std::move(temp);

Le compilateur va bien sûr de se débarrasser de la redondance d'affectation, laissant:

int* temp = x.pi;
x.pi = y.pi;
y.pi = temp;

Qui est exactement ce que notre coutume swap aurait fait en premier lieu. Ainsi, alors que avant C++0x je suis d'accord avec votre suggestion, personnalisés swaps'n'est pas vraiment la plus nécessaire, avec l'introduction de rvalue-références. std::swap fonctionnera parfaitement dans n'importe quelle classe qui implémente déplacer fonctions.

En fait, je dirais mise en œuvre d'un swap fonction doit devenir une mauvaise habitude. Toute classe qui aurait besoin d'un swap fonction aussi besoin de rvalue fonctions. Mais dans ce cas, il n'y a tout simplement plus besoin de vous encombrer d'une coutume swap. Code taille augmente (deux ravlue fonctions par rapport à un swap), mais rvalue-références ne s'applique pas seulement pour l'échange, nous laissant avec un positif hors commerce. (Dans l'ensemble plus rapide code, interface épurée, un peu plus de code, plus de swap ADL tracas.)

Quant à savoir si ou non nous pouvons default rvalue fonctions, je ne sais pas. Je vais regarder plus tard ou peut-être quelqu'un d'autre peut le carillon, mais qui serait certainement plus utile. :)

Même ainsi, il est logique d'autoriser default rvalue fonctions à la place de swap. Donc, en substance, à condition qu'elles autorisent = default rvalue fonctions, votre demande a déjà été faite. :)

EDIT: j'ai fait un peu de recherche, et la proposition d' = default déplacer des propositions n2583. Selon ce (que je ne sais pas comment lire très bien), il a été "déplacée vers l'arrière." Il est répertorié dans la section intitulée "Pas prêt pour C++0x, mais de l'ouvrir à nouveau dans le futur ". Regarde donc comme il ne sera pas une partie de C++0x, mais peut être ajouté plus tard.

Un peu décevant. :(

EDIT 2: en Cherchant un peu plus, j'ai trouvé ceci: Définition de Déplacer Spécial des Fonctions de Membre qui est beaucoup plus récente, et ne ressemble, nous pouvons par défaut move. Yay!!!

11voto

Terry Mahaffey Points 7368

swap, lorsqu'il est utilisé par les algorithmes de la STL, est une fonction libre. Il y a un échange sur défaut de mise en œuvre: std::swap. Il ne l'évidence. Vous semblez être sous l'impression que si vous ajoutez un swap de membre en fonction de votre type de données, des conteneurs STL et les algorithmes vont le trouver et l'utiliser. Ce n'est pas le cas.

Vous êtes censé se spécialisent std::swap (dans l'espace de noms à côté de votre type défini par l'utilisateur, de sorte qu'il est retrouvé par ADL) si vous pouvez faire mieux. Il est idiomatiques de l'avoir juste en remettre à un membre de la fonction d'échange.

Alors que nous sommes sur le sujet, il est également idiomatiques dans C++0x (en autant qu'il est possible d'avoir des expressions idiomatiques sur une nouvelle norme) pour mettre en œuvre rvalue constructeurs comme un échange.

Et oui, dans un monde où un membre de swap a été la langue de conception, au lieu d'une fonction libre échange, cela voudrait dire que nous aurions besoin d'un swap opérateur au lieu d'une fonction ou d'autre types primitifs (int, float, etc) n'a pas pu être traités de façon générique (comme ils n'ont pas de fonction de membre de swap). Alors pourquoi n'ont-ils pas le faire? Vous auriez à demander aux membres du comité pour sûr - mais je suis à 95% de certitude la raison en est que le comité a longtemps préféré la bibliothèque implémentation de fonctions lorsque cela est possible, inventer une nouvelle syntaxe pour mettre en œuvre une fonctionnalité. La syntaxe d'un swap de l'opérateur serait bizarre, parce que contrairement à d' =, +, -, etc, et tous les autres opérateurs, il n'est pas algébrique de l'opérateur tout le monde est familier avec pour le "swap".

C++ est un point de vue syntaxique assez complexe. Ils font de grands efforts pour ne pas ajouter de nouveaux mots clés ou de syntaxe fonctions lorsque cela est possible, et de le faire seulement pour de très bonnes raisons (lambdas!).

2voto

FredOverflow Points 88201

Est-ce que quelqu'un sait si de telles choses (folles?) Ont été suggérées au comité de normalisation C ++

Envoyer un email à Bjarne. Il connaît tout cela et répond habituellement en quelques heures.

1voto

UncleBens Points 24580

Est même généré par le compilateur constructeur de déplacement/d'affectation prévue (avec la valeur par défaut de mots clés)?

Si il est généré par le compilateur swap méthode implicite de la copie d'affectation l'opérateur peut être mis en œuvre à l'aide de copie-et-swap.

Même si le langage des feuilles de l'objet inchangé dans le cas d'exceptions, n'est-ce pas l'idiome, en exigeant la création d'un troisième objet, faire de chances d'échec plus grand en premier lieu?

Il peut également y avoir des implications sur les performances (la copie peut être plus cher que "l'attribution") c'est pourquoi je ne vois pas compliqué de ce type de fonctionnalité d'être de gauche pour être mis en œuvre par le compilateur.

En général je n'ai pas de surcharge de l'opérateur= et je ne vous inquiétez pas à ce niveau de l'exception de sécurité: je ne suis pas d'envelopper des affectations individuelles en blocs try - que ferais-je avec le plus de chances std::bad_alloc à ce point? - donc, je m'en foutrais si l'objet, avant qu'ils finissent de toute façon été détruit, est resté dans l'état d'origine ou pas. Il peut y avoir bien sûr des situations où vous pourriez en effet besoin, mais je ne vois pas pourquoi le principe de "vous n'avez pas à payer pour ce que vous n'utilisez pas" devrait être donnée jusqu'ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X