Як я можу передати std :: unique_ptr у функцію


97

Як я можу передати a std::unique_ptrу функцію? Скажімо, у мене є такий клас:

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

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

Наступне не компілюється:

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

Чому я не можу передати a std::unique_ptrу функцію? Напевно, це головне призначення конструкції? Або комітет С ++ мав намір, щоб я повернувся до необроблених покажчиків у стилі С і передав це так:

MyFunc(&(*ptr)); 

І що найдивніше, чому це нормальний спосіб проходження? Це здається жахливо суперечливим:

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

7
Немає нічого поганого в тому, щоб "повернутися" до сирих покажчиків у стилі С, якщо вони не є власниками сирих покажчиків. Можливо, ви віддасте перевагу написати 'ptr.get ()'. Хоча, якщо вам не потрібна дозвільність, посилання буде кращим.
Кріс Дрю,

Відповіді:


142

Тут в основному є два варіанти:

Передайте розумний вказівник за посиланням

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

Перемістіть розумний вказівник у аргумент функції

Зверніть увагу, що в цьому випадку твердження буде виконуватися!

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: Функцію, на яку ви посилаєтесь, зазвичай називають некопіюваною, а не унікальною.
Білл Лінч,

1
Дякую. У цьому випадку я хотів створити екземпляр, виконати з ним деякі дії, а потім передати право власності комусь іншому, для чого move () здається ідеальним.
user3690202

7
Передавати посилання слід лише в тому unique_ptrвипадку, якщо функція може рухатися з неї або не рухатися. І тоді це має бути посилання на rvalue. Щоб спостерігати за об’єктом, не вимагаючи нічого про його семантику власності, використовуйте посилання на зразок A const&або A&.
Potatoswatter

12
Послухайте розмову Herb Sutters на CppCon 2014. Він настійно не рекомендує передавати посилання на unique_ptr <>. Суть полягає в тому, що вам слід просто використовувати розумні вказівники, такі як unique_ptr <> або shared_ptr <>, коли ви маєте справу з правом власності. Дивіться www.youtube.com/watch?v=xnqTKD8uD64 Тут ви знайдете слайди github.com/CppCon/CppCon2014/tree/master/Presentations/…
schorsch_76

2
@ Фурір: Це лише спосіб показати, наскільки важливим ptrє переїзд.
Білл Лінч,

26

Ви передаєте його за значенням, що передбачає копіювання. Це було б не дуже унікально, правда?

Ви можете перемістити значення, але це передбачає передачу функції власності на об’єкт та контроль його тривалості життя.

Якщо час існування об'єкта гарантовано існуватиме протягом життя виклику MyFunc, просто передайте сирий вказівник ptr.get().


17

Чому я не можу передати a unique_ptrу функцію?

Ви не можете цього зробити, оскільки unique_ptrмає конструктор переміщення, але не конструктор копіювання. Відповідно до стандарту, коли конструктор переміщення визначений, але конструктор копіювання не визначений, конструктор копіювання видаляється.

12.8 Копіювання та переміщення об’єктів класу

...

7 Якщо у визначенні класу явно не оголошено конструктор копіювання, він оголошується неявно. Якщо визначення класу оголошує конструктор переміщення або оператор присвоєння переміщення, неявно оголошений конструктор копіювання визначається як видалений;

Ви можете передати unique_ptrфункцію, використовуючи:

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

і використовуйте його як у вас:

або

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

і використовувати його як:

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

Важлива примітка

Зверніть увагу, що якщо ви використовуєте другий спосіб, ptrне має права власності на вказівник після виклику std::move(ptr)return.

void MyFunc(std::unique_ptr<A>&& arg)матиме той самий ефект, що і void MyFunc(std::unique_ptr<A>& arg)оскільки обидва посилання.

У першому випадку ptrвсе ще має право власності на вказівник після виклику MyFunc.


5

Оскільки MyFuncвласність не береться, краще мати:

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

або краще

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

Якщо ви дійсно хочете отримати право власності, вам доведеться перенести свій ресурс:

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

або передати безпосередньо посилання на значення r:

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

std::unique_ptr не має спеціальної копії, щоб гарантувати наявність лише одного власника.


У цій відповіді відсутній вигляд дзвінка до MyFunc у перших двох випадках. Не впевнені, використовуєте ptr.get()чи просто передаєте ptrфункцію?
Тейлорстін

Для чого призначений підпис MyFuncдля передачі посилання на значення r?
Джеймс Гіршхорн,

@JamesHirschorn: void MyFunc(A&& arg)приймає посилання на значення r ...
Jarod42,

@ Jarod42 Це те, про що я здогадався, але з typedef int A[], MyFunc(std::make_unique<A>(N))видає помилку компілятора: error: недійсна ініціалізація посилання типу 'int (&&) []' із виразу типу 'std :: _ MakeUniq <int []> :: __ array' { aka 'std :: unique_ptr <int [], std :: default_delete <int []>>'} Чи g++ -std=gnu++11достатньо недавно?
Джеймс Гіршхорн,

@ Jarod42 Я підтвердив помилку для C ++ 14 з ideone: ideone.com/YRRT24
Джеймс Гіршхорн,

4

Чому я не можу передати a unique_ptrу функцію?

Ви можете, але не за допомогою копії - тому що std::unique_ptr<>неможливо створити копію.

Напевно, це головне призначення конструкції?

Крім усього іншого, std::unique_ptr<>призначений для однозначного позначення унікальної власності (на відміну від std::shared_ptr<>).

І що найдивніше, чому це нормальний спосіб проходження?

Оскільки в такому випадку копіювання не існує.


Дякую за вашу відповідь. Просто з інтересу, чи можете ви пояснити, чому останній спосіб його передачі не використовується конструктором копіювання? Я міг би подумати, що використання конструктора unique_ptr створить екземпляр у стеку, який буде скопійовано в аргументи для MyFunc () за допомогою конструктора копіювання? Хоча я визнаю, що мої спогади в цій області трохи розмиті.
user3690202

2
Оскільки це значення r, замість цього викликається конструктор переміщення. Хоча ваш компілятор, безумовно, все одно оптимізує це.
Nielk,

0

Так unique_ptrяк для унікального володіння, якщо ви хочете передати його як аргумент, спробуйте

MyFunc(move(ptr));

Але після цього стан ptrв Росії mainбуде nullptr.


4
"буде невизначеним" - ні, воно буде нульовим, або unique_ptrбуде скоріше марним.
TC

0

Передача функції std::unique_ptr<T>як значення не працює, оскільки, як ви вже згадуєте, unique_ptrне можна скопіювати.

Як що до цього?

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

цей код працює


Якщо цей код працює, тоді я припускаю, що він не копіює unique_ptr, а фактично використовує семантику переміщення для його переміщення.
user3690202

це може бути оптимізація поверненої вартості (RVO), я думаю
Ноумен Халікіне,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.