2016-01-19 7 views
10

Позвольте мне задать свой вопрос на примере.Почему при возвращении он не может быть автоматически преобразован в unique_ptr?

#include <memory> 

std::unique_ptr<int> get_it() { 
     auto p = new int; 
     return p; 
} 

int main() { 
     auto up (get_it()); 
     return 0; 
} 

Это не может скомпилировать со следующей ошибкой:

a.cpp:5:9: error: could not convert ‘p’ from ‘int*’ to ‘std::unique_ptr<int>’ 
    return p; 
     ^

Почему нет автоматического преобразования из исходного указателя на уникальный один здесь? И что я должен делать вместо этого?

Мотивация: Я понимаю, что хорошая практика - использовать интеллектуальные указатели для владения, чтобы быть ясным; Я получаю указатель (который у меня есть) где-то, как int* в этом случае, и я (думаю, я) хочу его в unique_ptr.


Если вы рассматриваете комментирования или добавления свой собственный ответ, пожалуйста, обращайтесь Herbert Sutter's arguments for this to be possible in proposal N4029.

+3

Потому что тогда такие вещи, как 'int x; return & x; 'будет компилироваться. –

+0

@KerrekSB: Я также могу выполнить 'int * p = nullptr; * p = 123; 'и это будет компилироваться. Во всяком случае, чтобы не быть слишком спорным, я спрошу - что мне делать? – einpoklum

+1

Вы делаете один из указателя, например. 'std :: unique_ptr (p)'. –

ответ

10

Ответ два раза. Все остальные ответы, в том числе самопомощь OP, касались только одной половины.

Указатель не может быть автоматически преобразован, потому что:

  • unique_ptr «s constructor из указателя объявляется explicit, таким образом, рассматривается компилятором только в явных контекстах.Это делается для предотвращения случайных опасных преобразований, где unique_ptr может захватить указатель и удалить его без знаний программиста. В целом, не только для unique_ptr, считается хорошей практикой объявлять все конструкторы с одним аргументом как explicit для предотвращения случайных преобразований.
  • Оператор возврата считается стандартным неявным контекстом, и поэтому явный конструктор неприменим. Продолжалась дискуссия, если это решение является правильным, что отражено в EWG issue 114, включая ссылки на несколько предложений: две версии предложений, которые должны сделать return явно Herb Sutter (N4029, N4074) и два «ответа», утверждая, что этого не делать: N4094 от Говарда Хиннанта и Вилле Вутилайнен и N4131 от Филиппа Розена. После нескольких обсуждений и опросов вопрос был закрыт как НАД - не дефект.

В настоящее время существует несколько обходных путей:

return std::unique_ptr<int>{p}; 

или

return std::unique_ptr<int>(p); 

В C++ 14, вы можете использовать автоматический вычет возврата функции типа, а также:

auto get_it() { 
    auto p = new int; 
    return std::unique_ptr<int>(p); 
} 

Обновление: добавлена ​​ссылка на вопрос комиссии для второго пункта.

+1

Благодарим вас за то, что вы предложили мне предложение г-на Саттера и обсуждение EWG, это наиболее освещающее (и я также должен сказать), в отличие от пренебрежительного отношения некоторых людей). – einpoklum

6

Потому что неявное построение unique_ptr от голого указателя было бы очень подверженным ошибкам.

Просто построить его в явном виде:

std::unique_ptr<int> get_it() { 
     auto p = new int; 
     return std::unique_ptr<int>(p); 
} 
+0

Не могу сказать, что я понимаю, как это может быть причиной ошибки в функции, возвращающей 'unique_ptr'. Можете ли вы подробнее рассказать? – einpoklum

+4

. 'foo (unique_ptr p) {}' в таком случае мог бы украсть пройденный голый указатель и уничтожить объект. – Stas

+0

Ваш пример не включает функцию, возвращающую unique_ptr, поэтому - я не следую ... – einpoklum

3

Поскольку std::unique_ptr становится владельцем указателя, и вы определенно не хотите, чтобы получить сырой указатель delete d случайно.

Если это было бы возможно, тогда:

void give_me_pointer(std::unique_ptr<int>) { /* whatever */ } 

int main() { 
    int *my_int = new int; 
    give_me_pointer(my_int); 
    // my_int is dangling pointer 
} 
+1

Это не причина ИМО. 'get_it' возвращает уникальный указатель, очевидно, что возвращается указатель из средства передачи права собственности. – einpoklum

+0

Хорошо, но я не думаю, что C++ достаточно умен, чтобы различать этот случай и случай в вопросе. Таким образом, оба разрешены или оба запрещены. – GingerPlusPlus

+0

@einpoklum: Это действительно причина. – ildjarn

2

Поскольку преобразование, или литье, требует соответствующего оператора литой (по типу источника) или конструктор (на целевом типа). В этом случае must be следующий конструктор unique_ptr:

explicit unique_ptr(pointer p); 

который имеет explicit keyword. OP get_it() пытается неявное преобразование, которое предотвращает explicit. Вместо этого, OP должен построить unique_ptr в явном виде, как это было предложено @Stas и @Vincent Савара:

std::unique_ptr<int> get_it() { 
     auto p = new int; 
     return std::unique_ptr<int>(p); 
} 

или даже, если мы хотим сказать только unique_ptr разы ...

auto get_it() { 
     auto p = new int; 
     return std::unique_ptr<int>(p); 
} 
+2

Простите меня, если это глупый вопрос, но почему бы не просто «make_unique»? – erip

+0

@erip: Потому что это MWE. На самом деле у нас есть 'auto p = do_stuff_which_returns_a_raw_pointer()' – einpoklum

+0

Правильно, но вы можете просто сделать 'auto get_it() {auto p = new int; return std :: make_unique (p); } ', не могли бы вы? Я бы подумал, что пересылка будет предпочтительнее явного вызова ctor. – erip

0

Потому что это опасно ,

Использование std::make_unique() от C++ 14:

std::unique_ptr<int> get_it() 
{ 
    auto p = std::make_unique<int>(); 
    return p; 
} 
+0

Прочтите мои комментарии к другим подобным ответам. – einpoklum