Std :: unique_ptr'yi bir işleve nasıl geçirebilirim


97

A'yı std::unique_ptrbir işleve nasıl geçirebilirim ? Diyelim ki şu sınıfa sahibim:

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

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

Aşağıdakiler derlenmez:

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;
}

Neden a'yı std::unique_ptrbir işleve geçiremiyorum? Elbette yapının birincil amacı bu mu? Veya C ++ komitesi ham C tarzı işaretlere geri dönmemi ve bunu şu şekilde aktarmamı mı istedi:

MyFunc(&(*ptr)); 

Ve en tuhafı, bu neden onu geçmenin iyi bir yolu? Korkunç derecede tutarsız görünüyor:

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

7
Ham işaretçilerin sahibi olmadıkları sürece, ham C tarzı işaretleyicilere "geri dönmenin" yanlış bir tarafı yoktur. 'Ptr.get ()' yazmayı tercih edebilirsiniz. Bununla birlikte, boş değer atanabilirliğe ihtiyacınız yoksa bir başvuru tercih edilir.
Chris Drew

Yanıtlar:


142

Burada temel olarak iki seçenek var:

Akıllı işaretçiyi referansla iletin

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);
}

Akıllı işaretçiyi işlev bağımsız değişkenine taşı

Bu durumda iddianın geçerli olacağını unutmayın!

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)
}

@VermillionAzure: Bahsettiğiniz özelliğe normalde kopyalanamaz denir, benzersiz değil.
Bill Lynch

1
Teşekkürler. Bu durumda bir örnek oluşturmak, üzerinde bazı işlemler yapmak ve ardından sahipliği başka birine devretmek istedim, bu da move () için mükemmel görünüyor.
user3690202

7
Bir unique_ptrişlevin hareket etmesi veya hareket etmemesi durumunda yalnızca referans olarak geçmelisiniz . Ve sonra bir rvalue referansı olmalıdır. Sahiplik semantiği hakkında hiçbir şey gerektirmeden bir nesneyi gözlemlemek için A const&veya gibi bir referans kullanın A&.
Potatoswatter

12
Herb Sutters'ın CppCon 2014'teki konuşmasını dinleyin. Unique_ptr <> 'ye atıfta bulunma konusunda kesinlikle cesaretini kırıyor. Esas olan, sahiplik ile uğraşırken sadece benzersiz_ptr <> veya paylaşılan_ptr <> gibi akıllı işaretçiler kullanmanız gerektiğidir. Burada slaytları bulabilirsiniz www.youtube.com/watch?v=xnqTKD8uD64 görün github.com/CppCon/CppCon2014/tree/master/Presentations/...
schorsch_76

2
@Furihr: Bu sadece ptrhareketten sonra değerinin ne olduğunu göstermenin bir yolu .
Bill Lynch

26

Onu değere göre geçiriyorsunuz, bu da bir kopya yapmak anlamına geliyor. Bu çok benzersiz olmaz, değil mi?

Değeri taşıyabilirsiniz, ancak bu, nesnenin sahipliğinin ve yaşam süresinin kontrolünün işleve aktarılması anlamına gelir.

MyFunc'a yapılan çağrının ömrü boyunca nesnenin yaşam süresinin var olacağı garanti edilirse, sadece ham bir gösterici üzerinden geçirin ptr.get().


17

Neden a'yı unique_ptrbir işleve geçiremiyorum?

Bunu yapamazsınız çünkü unique_ptrbir taşıma yapıcısı vardır, ancak kopya yapıcısı yoktur. Standarda göre, bir taşıma yapıcısı tanımlandığında ancak bir kopya oluşturucu tanımlanmadığında, kopya oluşturucu silinir.

12.8 Sınıf nesnelerini kopyalama ve taşıma

...

7 Sınıf tanımı bir kopya yapıcısını açık bir şekilde bildirmezse, biri örtük olarak bildirilir. Sınıf tanımı bir taşıma yapıcısı veya taşıma atama operatörü bildirirse, örtük olarak bildirilen kopya yapıcısı silinmiş olarak tanımlanır;

Şunu unique_ptrkullanarak işleve geçebilirsiniz :

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

ve sahip olduğunuz gibi kullanın:

veya

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

ve şu şekilde kullanın:

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

Önemli Not

İkinci yöntemi kullanırsanız, return ptrçağrısından sonra işaretçinin sahipliğinin olmadığını lütfen unutmayın std::move(ptr).

void MyFunc(std::unique_ptr<A>&& arg)void MyFunc(std::unique_ptr<A>& arg)her ikisi de referans olduğu için aynı etkiye sahip olacaktır .

İlk durumda, ptrçağrıldıktan sonra hala işaretçinin sahibi olur MyFunc.


5

As MyFuncsahipliğini almaz, bu olması daha iyi olacaktır:

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

ya da daha iyisi

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

Eğer gerçekten sahip olmak istiyorsanız, kaynağınızı taşımanız gerekir:

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

veya doğrudan bir r değeri referansı iletin:

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

std::unique_ptr tek bir sahibin olmasını garantilemek için bilerek kopyası yoktur.


Bu yanıt, ilk iki vakanız için MyFunc'a yapılan çağrının neye benzediğini eksik. Kullanıp kullanmadığınızdan ptr.get()veya sadece ptrişleve aktardığınızdan emin değil misiniz ?
taylorstine

MyFuncBir r-değeri referansı geçirmek için amaçlanan imza nedir ?
James Hirschorn

@JamesHirschorn: void MyFunc(A&& arg)r değeri referansını alır ...
Jarod42

@ Jarod42 ı ne tahmin Yani, fakat typedef int A[], MyFunc(std::make_unique<A>(N))hata: derleyici hata verir Çeşidi referans geçersiz başlatma 'int (&&) []' tür ekspresyonundan 'standart :: _ MakeUniq <int []> :: __ dizisi' { aka 'std :: unique_ptr <int [], std :: default_delete <int []>>'} g++ -std=gnu++11Yeterince yeni mi?
James Hirschorn

@ Jarod42 ideone ile C ++ 14 için başarısızlığı onayladım: ideone.com/YRRT24
James Hirschorn

4

Neden a'yı unique_ptrbir işleve geçiremiyorum?

Yapabilirsiniz, ancak kopyalayarak yapamazsınız - çünkü std::unique_ptr<>kopyalanabilir değildir.

Elbette yapının birincil amacı bu mu?

Diğer şeylerin yanı sıra, benzersiz sahipliği std::unique_ptr<>kesin olarak işaretlemek için tasarlanmıştır (aksine ).std::shared_ptr<>

Ve en tuhafı, bu neden onu geçmenin iyi bir yolu?

Çünkü bu durumda kopya-inşa yoktur.


Cevabınız için teşekkürler. İlgilenmeden, onu geçmenin son yolunun neden kopya yapıcısını kullanmadığını açıklayabilir misiniz? Unique_ptr yapıcısının kullanımının, kopya oluşturucusunu kullanarak MyFunc () argümanlarına kopyalanacak yığın üzerinde bir örnek oluşturacağını düşünürdüm. Bu alanda hatırladığımın biraz belirsiz olduğunu kabul etsem de.
user3690202

2
Bir rvalue olduğu için, bunun yerine hareket-yapıcı çağrılır. Derleyiciniz bunu kesinlikle optimize edecek olsa da.
Nielk

0

Çünkü unique_ptrbenzersiz sahiplik için, bunu argüman olarak iletmek istiyorsanız deneyin

MyFunc(move(ptr));

Ama devlet bundan sonra ptrin mainolacaktır nullptr.


4
"tanımsız olacak" - hayır, boş olacak veya unique_ptrfaydasız olacaktır.
TC

0

Geçme std::unique_ptr<T>bir işleve değer çünkü çalışmıyor gibi adamlar söz olarak, unique_ptrcopyable değildir.

Peki buna ne dersin?

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

bu kod çalışıyor


Eğer bu kod işe yararsa, o zaman benim tahminim, benzersiz_ptr'yi kopyalamak değil, aslında onu taşımak için hareket semantiğini kullanmaktır.
user3690202

Geri Dönüş Değeri Optimizasyonu (RVO) olabilir sanırım
Noueman Khalikine
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.