C++ Использует ли ссылки auto_ptr как переменные вне идиоматических?

Предположим, я хочу написать фабричный метод, который должен распределять гетерогенные объекты в куче и возвращать их вызывающей стороне. Я думаю о разработке API следующим образом:

bool MakeEm(auto_ptr<Foo>& outFoo, auto_ptr<Bar>& outBar) {
  ...
  if (...) {
    return false;
  }
  outFoo.reset(new Foo(...));
  outBar.reset(new Bar(...));
  return true;
}

Это позволяет вызывающей стороне сделать это:

auto_ptr<Foo> foo;
auto_ptr<Bar> bar;
MakeEm(foo, bar);

Мой вопрос: "Это идиоматизм? Если нет, то как правильно это сделать?"

Альтернативные подходы, которые я могу придумать, включают возвращение struct из auto_ptrs, или написание фабричного API для получения необработанных ссылок на указатели. Они оба требуют написания большего количества кода, и у последнего есть другие проблемы, когда речь идет о безопасности исключений.

5 ответов

Решение

Вам не нужно создавать собственную структуру для возврата двух значений - вы можете использовать std::pair. В этом случае нет большой синтаксической нагрузки при возврате двух значений. Это решение имеет проблему, заключающуюся в том, что ".first" и ".second" не являются очень описательными именами, но если используемые типы и имя функции проясняют намерение, то это не обязательно проблема.

Если вы используете C++0x, вы можете использовать unique_ptr вместо auto_ptr, и вызывающая сторона может использовать auto вместо того, чтобы набирать более длинный std:: pair, std:: unique_ptr>. Если вы не используете C++0x, вы можете использовать вместо этого typedef.

Если вы вернете два значения, у вас не будет места для bool. Вы можете использовать кортеж C++0x для возврата всех трех значений. Вы также можете указать ошибку, выдав исключение или вернув нулевые указатели. Я предпочел бы исключение, предполагая, что ошибка редкая / исключительная.

Как указывали другие ответы, часто предпочтительно иметь две отдельные функции, каждая из которых возвращает один объект. Если вы не можете сделать это, потому что инициализация двух объектов неразрывно связана, то вы можете создать класс, который инкапсулирует инициализацию. Вы можете передать необходимую информацию, чтобы сделать два объекта конструктору (требуется исключение для сигнализации об ошибках), а затем иметь два метода в этом классе, которые выдают по одному объекту каждый.

Задание чего-то идиоматического может дать вам очень субъективные ответы. В целом, однако, я думаю, что auto_ptr - это отличный способ передать право собственности, так как возвращение с фабрики классов - это, вероятно, хорошая вещь. Я хотел бы рефакторинг этого, так что

  1. Вы возвращаете один объект вместо 2. Если вам нужны два тесно связанных между собой объекта, которые не могут существовать друг без друга, я бы сказал, что у вас есть веские основания для рефакторинга is-a или has-has.
  2. Это C++. Действительно спросите себя, должны ли вы возвращать значение, указывающее на успех, заставляя потребителя вашего завода каждый раз проверять. Бросайте исключения или передавайте исключения из конструкторов ваших классов на фабрике. Хотели бы вы когда-нибудь быть в порядке с false и попытаться оперировать неинициализированным auto_ptr?

Как правило, если это связано с auto_ptrне идиоматично. В общем, структура не слишком идиоматична - обычно вы создаете одну функцию для каждой, возвращаете значение и выдает исключение, если они терпят неудачу, и если вам нужно совместно использовать переменные, делайте это объектом.

Давайте предположим, что возвращаемое значение false означает "не смотрите на выходные параметры".

Затем я бы избавился от возвращаемого значения bool, вернул структуру или пару, в которой были бы нужные вам auto_pointers, и throw в состоянии ошибки.

Обычно, когда у вас есть параметры auto_ptr, они не являются ссылками.

Это потому, что когда вы передаете что-то функции, которая принимает auto_ptr, вы ожидаете, что эта функция станет владельцем. Если вы передаете по ссылке, он на самом деле не берет объект (он может взять объект).

Это тонкий момент, но, в конце концов, вам нужно посмотреть, что ваш интерфейс пытается сказать пользователю.

Также вы, кажется, используете его как выходной параметр.
Лично я никогда не видел этот вариант использования (но я вижу это), просто документируйте, что вы пытаетесь сделать, и, что более важно, почему.

Другие вопросы по тегам