web-dev-qa-db-fra.com

Comment puis-je passer std :: unique_ptr dans une fonction

Comment puis-je passer un std::unique_ptr dans une fonction? Disons que j'ai la classe suivante:

class A
{
public:
    A(int val)
    {
        _val = val;
    }

    int GetVal() { return _val; }
private:
    int _val;
};

Ce qui suit ne compile pas:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);

    return 0;
}

Pourquoi ne puis-je pas passer un std::unique_ptr dans une fonction? C’est sûrement le but principal de la construction? Ou bien le comité C++ avait-il l'intention de me rabattre sur des pointeurs bruts de style C et de le transmettre comme suit:

MyFunc(&(*ptr)); 

Et le plus étrange de toutes, pourquoi est-ce une bonne façon de le faire passer? Cela semble horriblement incohérent:

MyFunc(unique_ptr<A>(new A(1234)));
54
user3690202

Il y a essentiellement deux options ici:

Passer le pointeur intelligent par référence

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

Déplacer le pointeur intelligent dans l'argument de la fonction

Notez que dans ce cas, l'assertion sera vérifiée!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}
84
Bill Lynch

Vous le transmettez par valeur, ce qui implique de faire une copie. Ce ne serait pas très unique, n'est-ce pas?

Vous pouvez déplacer la valeur, mais cela implique de passer la propriété de l'objet et le contrôle de sa durée de vie à la fonction.

Si la durée de vie de l'objet est garantie pendant toute la durée de l'appel de MyFunc, il suffit de passer un pointeur brut via ptr.get().

19
Mark Tolonen

Pourquoi ne puis-je pas passer un unique_ptr Dans une fonction?

Vous ne pouvez pas faire cela parce que unique_ptr A un constructeur de déplacement mais pas un constructeur de copie. Selon la norme, lorsqu'un constructeur de déplacement est défini mais qu'un constructeur de copie n'est pas défini, le constructeur de copie est supprimé.

12.8 Copier et déplacer des objets de classe

...

7 Si la définition de classe ne déclare pas explicitement un constructeur de copie, celui-ci est déclaré implicitement. Si la définition de classe déclare un constructeur de déplacement ou un opérateur d'affectation de déplacement, le constructeur de copie implicitement déclaré est défini comme supprimé.

Vous pouvez passer le unique_ptr À la fonction en utilisant:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

et utilisez-le comme vous l'avez:

ou

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

et l'utiliser comme:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

note importante

Veuillez noter que si vous utilisez la deuxième méthode, ptr n'est pas propriétaire du pointeur après l'appel de std::move(ptr).

void MyFunc(std::unique_ptr<A>&& arg) aurait le même effet que void MyFunc(std::unique_ptr<A>& arg) puisque les deux sont des références.

Dans le premier cas, ptr est toujours propriétaire du pointeur après l'appel à MyFunc.

13
R Sahu

Pourquoi ne puis-je pas passer un unique_ptr dans une fonction?

Vous pouvez le faire, mais pas par copie, car std::unique_ptr<> n'est pas copiable.

C’est sûrement le but principal de la construction?

Entre autres, std::unique_ptr<> est conçu pour marquer sans équivoque nique propriété (par opposition à std::shared_ptr<>).

Et le plus étrange de toutes, pourquoi est-ce une bonne façon de le faire passer?

Parce que dans ce cas, il n'y a pas de construction de copie.

4
Nielk

Comme MyFunc ne s'en approprie pas, il serait préférable d'avoir:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

ou mieux

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

Si vous voulez vraiment vous approprier, vous devez déplacer votre ressource:

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

ou passez directement une référence de valeur r:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr n'a pas exprès de copie pour garantir d'avoir un seul propriétaire.

4
Jarod42

Puisque unique_ptr est la propriété unique, si vous voulez passer en argument, essayez

MyFunc(move(ptr));

Mais après cela, l'état de ptr dans main sera nullptr.

0
0x6773