Каковы причины, по которым конструктор std::unique ptr из T* является явным?


Поскольку std::unique_ptr предоставляет удобный способ избежать утечек памяти и обеспечить безопасность исключений, разумно передавать их, а не сырые указатели. Таким образом, может потребоваться (член) функции с сигнатурой типа

std::unique_ptr<some_type> foo(some data);

К сожалению, при реализации такой функции нельзя просто

std::unique_ptr<some_type> foo(some data)
{
  return { new some_type(data) };                  // error
}

Но должен вместо этого

std::unique_ptr<some_type> foo(some data)
{
  return std::move( std::unique_ptr<some_type>( new some_type(data) ) );   // awkward
}

Потому что конструктор unique_ptr::unique_ptr(pointer) - это explicit. Каковы рассуждения, стоящие за этим конструктором explicit?

Одна мотивация, чтобы сделать конструкторы explicit предназначены для защиты от непреднамеренного неявного преобразования типов. Однако, поскольку unique_ptr не может быть передано по значению, это не должно быть проблемой, не так ли?

2 12

2 ответа:

unique_ptr принимает во владение переданный указатель. Принятие права собственности должно быть явным - вы не хотите, чтобы какой-то указатель "волшебным образом" стал собственностью (и был удален) каким-то классом (это была одна из проблем с устаревшим std::auto_ptr).

Например:

void fun(std::unique_ptr<X> a) { .... }
X x;
fun(&x); // BOOM, deleting object on stack, fortunately it does not compile
fun(std::unique_ptr<X>(&x)); // compiles, but it's explicit and error is clearly visible
Обратите внимание, что std::move не требуется в операторе return (исключение специального языка - локальные переменные в качестве аргументов return могут рассматриваться как "перемещенные").

Также-в C++14 вы можете использовать std::make_unique чтобы сделать его меньше неудобно:

return std::make_unique<some_data>(some_data_argument1, arg2);

(его также можно легко добавить в C++11-читать здесь)

Аргументы, принимающие уникальный ptr, не должны молча принимать на себя ответственность за указатели.

Таким образом, ctor является явным.

Чтобы вернуться, попробуйте make_unique<foo>(?) вместо {new foo(?)}.