web-dev-qa-db-fra.com

Façon idiomatique pour empêcher le tranchage?

Parfois, cela peut être une gêne que c ++ utilise par défaut pour permettre le découpage. Par exemple

struct foo { int a; };
struct bar : foo { int b; };

int main() {
    bar x{1,2};
    foo y = x; // <- I dont want this to compile!
}

Ceci compile et s'exécute comme prév ! Et si je ne veux pas activer le découpage?

Quelle est la manière idiomatique d'écrire foo de sorte que l'on ne peut pas découper les instances d'une classe dérivée?

17
idclev 463035818

Je ne sais pas s'il existe un idiome nommé pour cela, mais vous pouvez ajouter une fonction supprimée à l'ensemble de surcharge qui correspond mieux que les opérations de découpage des classes de base. Si vous remplacez foo par

struct foo 
{ 
    int a; 
    foo() = default; // you have to add this because of the template constructor

    template<typename T>
    foo(const T&) = delete; // error trying to copy anything but a foo

    template<typename T>
    foo& operator=(const T&) = delete; // error assigning anything else but a foo
};

alors vous ne pouvez copier que construire ou copier assigner un foo à foo. Tout autre type choisira le modèle de fonction et vous obtiendrez une erreur sur l'utilisation d'une fonction supprimée. Cela signifie cependant que votre classe et les classes qui l'utilisent ne peuvent plus être un agrégat. Étant donné que les membres ajoutés sont des modèles, ils ne sont pas considérés comme des constructeurs de copie ou des opérateurs d'affectation de copie, vous obtiendrez donc les constructeurs et opérateurs d'affectation de copie et de déplacement par défaut.

20
NathanOliver

Depuis 2011, la manière idiomatique est d'utiliser auto:

#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };

int main() {
    bar x{1,2};
    auto y = x; // <- y is a bar
}

Si vous souhaitez empêcher activement le tranchage, il existe plusieurs façons:

Habituellement, la méthode la plus préférable, sauf si vous avez spécifiquement besoin de l'héritage (ce n'est souvent pas le cas), est d'utiliser l'encapsulation:

#include <iostream>

struct foo { int a; };
struct bar 
{ 
    bar(int a, int b)
    : foo_(a)
    , b(b)
    {}

    int b; 

    int get_a() const { return foo_.a; }

private:
    foo foo_;
};

int main() {
    bar x{1,2};
//    foo y = x; // <- does not compile

}

Une autre façon plus spécialisée pourrait être de modifier les autorisations des opérateurs de copie:

#include <iostream>

struct foo { 
    int a; 
protected:
    foo(foo const&) = default;
    foo(foo&&) = default;
    foo& operator=(foo const&) = default;
    foo& operator=(foo&&) = default;

};

struct bar : foo
{ 
    bar(int a, int b) 
    : foo{a}, b{b}
    {}

    int b; 
};

int main() {
    auto x  = bar (1,2);
//    foo y = x; // <- does not compile
}
5
Richard Hodges

Vous pouvez empêcher la copie de la base en dehors des fonctions membres des classes dérivées et de la base elle-même en déclarant le constructeur de copie protégé:

struct foo {
    // ...
protected:
    foo(foo&) = default;
};
3
eerorika