web-dev-qa-db-fra.com

Signification de = supprimer après la déclaration de fonction

class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

Que signifie = delete dans ce contexte?

Existe-t-il d'autres "modificateurs" (autres que = 0 et = delete)?

221
Pat O'Keefe

Supprimer une fonction est ne fonctionnalité C++ 11 :

Le langage courant consistant à "interdire la copie" peut maintenant être exprimé directement:

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

Le mécanisme "delete" peut être utilisé pour n'importe quelle fonction. Par exemple, nous pouvons éliminer une conversion non désirée comme ceci:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};
182
Prasoon Saurav
  1. = 0 signifie qu'une fonction est virtuelle et que vous ne pouvez pas instancier un objet de cette classe. Vous devez en dériver et implémenter cette méthode
  2. = delete signifie que le compilateur ne générera pas ces constructeurs pour vous. Autant que je sache, cela n’est autorisé que sur le constructeur de copie et sur l’opérateur d’affectation. Mais je ne suis pas trop bon pour la norme à venir.
76
mkaes

Cet extrait de Le livre sur le langage de programmation C++ [4e édition] - Bjarne Stroustrup parle du réel but derrière l'utilisation de =delete:

L'utilisation de la copie ou du déplacement par défaut d'une classe dans une hiérarchie est généralement un sinistre: étant donné qu'un pointeur sur une base, nous ne savons tout simplement pas quels sont les membres de la classe dérivée (§3.2.2) , donc nous ne savons pas comment les copier . La meilleure chose à faire est donc généralement de supprimer les opérations de copie et de déplacement par défaut, c'est-à-dire d'éliminer les définitions par défaut de ces deux opérations:

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

Maintenant, une tentative de copier une forme sera capturée par le compilateur.

Le mécanisme =delete est général, c’est-à-dire qu’il peut être utilisé pour supprimer toute opération.

20
Saurav Sahu

Existe-t-il d'autres "modificateurs" (autres que = 0 et = delete)?

Comme il semble que personne d’autre n’ait répondu à cette question, je dois mentionner qu’il existe également =default.

https://docs.Microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions

7
Kyle Delaney

= delete est une fonctionnalité introduite dans C++ 11. Selon =delete il ne sera pas autorisé à appeler cette fonction.

En détail.

Suppose dans une classe.

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

Lors de l'appel de cette fonction pour l'assignation obj, cela ne sera pas autorisé. L'opérateur d'affectation de moyens va restreindre la copie d'un objet à un autre.

3
ashutosh

Les normes de codage avec lesquelles j'ai travaillé ont les caractéristiques suivantes pour la plupart des déclarations de classe.

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

Si vous utilisez l'un de ces 6, vous commentez simplement la ligne correspondante.

Exemple: la classe FizzBus nécessite seulement dtor, et n’utilisez donc pas les 5 autres.

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

Nous ne commentons que 1 ici, et installons son implémentation ailleurs où (probablement où la norme de codage le suggère). Les 5 autres (sur 6) ne sont pas autorisés avec delete.

Vous pouvez également utiliser '= delete' pour interdire les promotions implicites de valeurs de tailles différentes ... exemple

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
3
2785528

Nouveau standard C++ 0x. Veuillez vous reporter à la section 8.4.3 du brouillon de travail N3242

2
dubnde

Une fonction supprimée est implicitement inline

[Addendum aux réponses existantes)

... Et une fonction supprimée sera la première déclaration de la fonction (à l'exception de la suppression des spécialisations explicites des modèles de fonction - la suppression doit être effectuée à la première déclaration de la spécialisation), ce qui signifie que vous ne pouvez pas déclarer une fonction et la supprimer ultérieurement, par exemple: à sa définition locale à une unité de traduction.

Citer [dcl.fct.def.delete]/4 :

Une fonction supprimée est implicitement inline. (Remarque: La règle de définition unique ( [basic.def.odr] ) s'applique aux définitions supprimées. - note de fin] Une définition supprimée d'une fonction doit être la première déclaration de la fonction ou, pour une spécialisation explicite d'un modèle de fonction, la première déclaration de cette spécialisation. [Exemple:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

- exemple de fin)

Un modèle de fonction principal avec un définition supprimé peut être spécialisé

Bien que la règle générale soit pour éviter de spécialiser des modèles de fonction , étant donné que les spécialisations ne participent pas à la première étape de la résolution de la surcharge, il existe des contextes discutables dans lesquels cela peut être utile. Par exemple. lorsqu’on utilise un modèle de fonction primaire non surchargé sans définition pour correspondre à tous les types que l’on ne voudrait pas convertir implicitement en une surcharge par correspondance; c'est-à-dire, supprimer implicitement un certain nombre de correspondances de conversion implicite en implémentant uniquement des correspondances de type exactes dans la spécialisation explicite du modèle de fonction principale non défini et non surchargé.

Avant le concept de fonction supprimé de C++ 11, on pouvait le faire en omettant simplement la définition du modèle de fonction principal, mais cela donnait des erreurs obscures référence non définie qui ne donnaient sans doute aucune intention sémantique de l'auteur. du modèle de fonction principal (omis intentionnellement?). Si au lieu de cela nous supprimons explicitement le modèle de fonction primaire, les messages d'erreur si aucune spécialisation explicite appropriée n'est trouvée deviennent beaucoup plus agréables et indiquent également que l'omission/la suppression de la définition du modèle de fonction primaire était intentionnelle.

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

Toutefois, au lieu d'omettre simplement une définition du modèle de fonction primaire ci-dessus, générant une erreur de référence obscure non définie lorsque aucune spécialisation explicite ne correspond, la définition du modèle principal peut être supprimée:

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

Produire un message d'erreur plus lisible, dans lequel l'intention de suppression est également clairement visible (où une erreur référence non définie pourrait amener le développeur à penser que c'est une erreur involontaire).

Revenons à pourquoi voudrions-nous jamais utiliser cette technique? De nouveau, des spécialisations explicites pourraient être utiles pour implicitement ​​supprimer les conversions implicites.

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}
0
dfri

C'est une nouvelle chose dans les normes C++ 0x où vous pouvez supprimer une fonction héritée.

0
Tayyab