c++ {*ce} à l'intérieur des accolades

Le code suivant compile bien:

g++ -std=c++11 test.cpp -Wall -Wextra -Wfatal-errors && ./a.out

Cependant, si je supprime les accolades de {*this} et utilise *this à la place, je vais faire face à une erreur:

Erreur: utilisation de la fonction supprimée 'Obj:: Position:: Position(Obj:: Position&&)'

Quelle est la différence entre {*this} et *this?

class Obj
{
    template<bool> friend class Position;

    double data;
public:
    class Position
    {
        const Obj& ref;
    public:
        inline Position(const Obj& ref): ref(ref){}
        inline Position(Position const &) = delete;
        inline Position(Position &&) = delete;
    };
    inline Obj(){}
    inline Obj(const double &data): data(data){}
    inline auto get_pos() const-> Position{return {*this};} /* <--- here */
    inline auto get_pos()-> Position{return {*this};}
};

int main()
{
    return 0;
}
50
demandé sur ar2015 2017-08-08 03:13:13

4 réponses

La différence entre les deux est vraiment assez subtile. C++11 introduit la fonction initialisation de la liste (aussi parfois appelé accolade de l'initialisation):

Avant C++11, lorsque vous voulez par défaut-la construction et l'objet o de type Obj et construire un Position p à partir de o, vous avez dû vous écrire

Obj o;              // default construct o
Obj::Position p(o); // construct p using Position(Obj const&)

Une erreur courante pour les débutants (surtout avec un fond Java) était d'essayer d'écrire ceci:

Obj o();            // mistake: declares a function o returning an Obj
Obj::Position p(o); // error: no constructor takes a function 

La première ligne déclare un function , et le second essaie de créer un Position en utilisant un constructeur qui prend un pointeur de fonction comme argument. Afin d'avoir une syntaxe d'initialisation uniforme, C++11 introduit l'initialisation de liste:

Obj oo{};             // new in C++11: default construct o of type Obj
Obj::Position p1(oo); // possible before (and after) C++11
Obj::Position p2{oo}; // new in C++11: construct p2 using Position(Obj const&)

Cette nouvelle syntaxe fonctionne également dans return - instructions, et cela conduit à la réponse à votre question: la différence entre return {*this}; et return *this; est que le premier initialise la valeur de retour directement de *this, alors que le dernier convertit d'abord *this en un objet temporaire Position, puis initialise indirectement la valeur de retour à partir de ce temporaire , qui échoue car les constructeurs copy et move ont été explicitement supprimés.

Comme l'ont noté les affiches précédentes, la plupart des compilateurs élide ces objets temporaires car ils ne sont vraiment utiles à rien; mais cela n'est possible que s'ils peuvent être utilisés en théorie car un constructeur copy ou move est disponible. Parce que cela conduit à beaucoup de confusion (pourquoi ai-je besoin d'accolades autour de ma déclaration de retour? est le compilateur va éluder la copie ou pas?), C++17 supprime ces temporaires inutiles, et initialise la valeur de retour directement dans les deux cas (return {*this}; et return *this).

Vous pouvez essayer cela en utilisant un compilateur qui prend en charge C++17. Dans clang 4.0 ou gcc 7.1, vous pouvez passer --std=c++1z, et votre code doit être compilé correctement avec et sans accolades.

15
répondu Tobias 2017-08-10 21:13:28

Lorsque les accolades sont présentes, vous êtes copier-liste-initialisation la valeur de retour, aucun constructeur copy / move n'est impliqué. La valeur de retour est construite sur place en utilisant le constructeur Position(const Obj&).

Notez que le code échouerait à compiler même avec les accolades si vous avez créé le constructeur Position(const Obj&) explicitcar copy-list-initialization ne permet pas d'appeler des constructeurs explicites.

Si vous omettez les accolades, alors sémantiquement, un objet temporaire Position est construit dans la fonction, et la valeur de retour est move construit à partir de ce temporaire. En pratique, la plupart des implémentations éliront la construction move, mais cela nécessite toujours la présence d'un constructeur move viable, ce qui n'est pas le cas ici car il a été explicitement supprimé. C'est la raison pour laquelle votre code ne compilera pas sans accolades.

En utilisant un compilateur C++17, votre code compilera même sans les accolades à cause de garanti copier-élision.

37
répondu Praetorian 2017-08-08 00:51:18

C'est un bon! En effet, return {...} signifie "retourner un objet du type de retour de la fonction initialisé avec list initializer ...".

Les initialiseurs de liste sont décrits plus en détail ici:

Http://en.cppreference.com/w/cpp/language/list%20initialization

Donc, la différence est que {*this} appelle cela:

inline Position(const Obj& ref): ref(ref){}

Alors que *this essaie de convertir Obj& en Position en utilisant des opérateurs d'affectation explicitement supprimés (avant C++11, ils devraient être fait private, et vous obtiendriez un message d'erreur encore plus confus si les initialiseurs de liste seraient disponibles...):

inline Position(Position const &) = delete;
inline Position(Position &&) = delete;
13
répondu isp-zax 2017-08-08 01:17:30

Franchement, en utilisant votre classe et la main () suivante:

int main()
{
    Obj o1;
    cout<<"get position"<<'\n';
    Obj::Position pos= o1.get_pos();


    cout.flush();
    return 0;
}

Il ne compile pas (gcc/mingw) dans les deux cas (- std = C++14), avec ou sans accolades et il se plaint de la position manquante(Position&&) constructeur, qui est supprimé. C'est raisonnable car il semble que dans les deux cas une construction de l'objet de retour temporaire est effectuée, qui doit ensuite être déplacée vers la destination. Ce qui est impossible car move constructor est supprimé. Inversement, en utilisant -std = c++17 le signaler compile dans les deux cas (avec ou sans accolades) car, très probablement, nous atteignons l'optimisation de la valeur de Retour Garantie de c++17. Espérons que cette aide.

-2
répondu Sandro 2017-08-08 17:13:07