Что Вы думаете о выдаче исключения для не найденный в C++?

Это была просто проблема с функцией, я передал ей 1 параметр, но ожидал 2 параметра, поэтому сборка не удалась.

12
задан Edwin Jarvis 27 September 2008 в 21:05
поделиться

11 ответов

STL справляется с этой ситуацией при помощи итераторов. Например, станд.:: класс сопоставления имеет подобную функцию:

iterator find( const key_type& key );

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

16
ответ дан 2 December 2019 в 04:54
поделиться

Корректный ответ (по данным Alexandrescu):

Optional и Осуществите

В первую очередь, действительно используйте Средство доступа, но более безопасным способом, не изобретая колесо:

boost::optional<X> get_X_if_possible();

Затем создайте enforce помощник:

template <class T, class E>
T& enforce(boost::optional<T>& opt, E e = std::runtime_error("enforce failed"))
{
    if(!opt)
    {
        throw e;
    }

    return *opt;
}

// and an overload for T const &

Таким образом, в зависимости от того, что могло бы отсутствие среднего значения, Вы любая проверка явно:

if(boost::optional<X> maybe_x = get_X_if_possible())
{
    X& x = *maybe_x;

    // use x
}
else
{
    oops("Hey, we got no x again!");
}

или неявно:

X& x = enforce(get_X_if_possible());

// use x

Вы используете первый путь, когда Вы обеспокоены эффективностью, или когда Вы хотите обработать право отказа, где это происходит. Второй путь для всех других случаев.

6
ответ дан 2 December 2019 в 04:54
поделиться

Проблема с существует (), то, что Вы закончите тем, что искали дважды вещи, которые действительно существуют (сначала проверка, если это там, затем найдите его снова). Это неэффективно, особенно если (поскольку его название "списка" предлагает) Ваш контейнер является тем, где поиск является O (n).

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

5
ответ дан 2 December 2019 в 04:54
поделиться

Не используйте исключение в таком случае. C++ имеет нетривиальную производительность наверху для таких исключений, даже если никакое исключение не выдается, и он additially делает обоснование о коде намного тяжелее (cf. безопасность исключения).

Лучшая практика в C++ является одним из двух после путей. Оба привыкают в STL:

  • Как Martin указал, возвратите итератор. На самом деле Ваш итератор может хорошо быть a typedef для простого указателя нет ничего, выступая против него; на самом деле, так как это согласовывается с STL, Вы могли даже утверждать, что этот путь превосходит возврат ссылки.
  • Возвратите a std::pair<bool, yourvalue>. Это лишает возможности изменять значение, тем не менее, начиная с copycon pair назван, который не работает с участниками референдумов.

/ РЕДАКТИРОВАНИЕ:

Этот ответ породил некоторое противоречие, видимое из комментариев и не настолько видимый от многих downvotes, которые это получило. Я нашел это довольно удивительным.

Этот ответ никогда не предназначался как окончательный ориентир. “Корректный” ответ был уже дан Martin: execeptions отражают поведение в этом случае скорее плохо. Это семантически более значимо для использования некоторого другого сигнального механизма, чем исключения.

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

На самом деле я упомянул два фасета: производительность и безопасность исключения. Я полагаю, что последний является довольно бесспорным. В то время как чрезвычайно трудно дать сильные гарантии исключений (самое сильное, конечно, будучи “nothrow”), я полагаю, что это важно: любой код, который, как гарантируют, не выдаст исключения, делает целую программу легче рассуждать о. Многие эксперты C++ подчеркивают это (например, Scott Meyers в объекте 29 из “Эффективного C++”).

О скорости. Martin York указал, что это больше не применяется в современных компиляторах. Я почтительно не соглашаюсь. Язык C++ заставляет среду отслеживать, во времени выполнения, путей выполнения кода, которые могут быть раскручены в случае исключения. Теперь, эти издержки не действительно все, что большой (и довольно легко проверить это). “нетривиальный” в моем выше текста, возможно, было слишком сильно.

Однако я нахожу важным потянуть различие между языками как C++ и многими современными, “управляемыми” языками как C#. У последнего нет дополнительных издержек, пока никакое исключение не выдается, потому что информация, необходимая для раскручивания стека, хранится так или иначе. В общем и целом поддержите мой выбор слов.

5
ответ дан 2 December 2019 в 04:54
поделиться

Как насчет того, чтобы возвратить shared_ptr как результат. Это может быть пустым, если объект не был найден. Это работает как указатель, но это будет заботиться о выпуске объекта для Вас.

1
ответ дан 2 December 2019 в 04:54
поделиться

Итераторы STL?

Идея "итератора", предложенная передо мной, интересна, но основное назначение итераторов является навигацией через контейнер. Не как простое средство доступа.

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

Который приводит нас к...

Интеллектуальные указатели?

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

Необходимо выбрать: Или Ваше Значение уже в общем указателе, и затем, можно возвратить этот общий указатель (или слабый указатель). Или Ваше значение в необработанном указателе. Затем можно возвратить указатель строки. Вы не хотите возвращать общий указатель, если Ваш ресурс уже не в общем указателе: Мир забавных вещей произойдет, когда Ваш общий указатель выйдет из объема удаление Вашего Значения, не говоря Вам...

:-p

Указатели?

Если Ваш интерфейс соглашается со своим владением его ресурсов, и фактом возвращенное значение может быть НУЛЕВЫМ, то Вы могли возвратить простой, необработанный указатель. Если пользователь Вашего кода является достаточно немым, игнорируют интерфейсный контракт Вашего объекта, или играть в арифметику или безотносительно с Вашим указателем, то он будет достаточно немым для повреждения любого другого способа, которым Вы примете решение возвратить значение, не беспокойтесь мысленно оспариваемый...

Неопределенное значение

Если Ваш тип Значения действительно уже не имеет некоторое "неопределенное" значение, и пользователь знает, что, и примет для обработки этого, это - возможное решение, подобное решению для итератора или указателю.

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

Исключения

Исключения являются обычно не столь дорогостоящими, как некоторые люди хотели бы, чтобы они были. Но для простого средства доступа, стоимость могла быть не тривиальной, если Ваше средство доступа часто используется.

Например, станд. STL:: вектор имеет два средства доступа к своему значению через индекс:

T & std::vector::operator[]( /* index */ )

и:

T & std::vector::at( /* index */ )

Причем различие то, что [] не бросает. Так, если Вы получаете доступ вне диапазона вектора, Вы самостоятельно, вероятно, рискуете повреждением памяти и катастрофическим отказом рано или поздно. Так, необходимо действительно быть уверены, что Вы проверили код с помощью него.

С другой стороны, at бросает. Это означает, что, если Вы получаете доступ вне диапазона вектора, затем Вы получите чистое исключение. Этот метод лучше, если Вы хотите делегировать к другому коду обработку ошибки.

Я использую personnaly [] когда я получаю доступ к значениям в цикле или чему-то подобному. Я использую at когда я чувствую, что исключением является хороший способ возвратить текущий код (или код вызова) факт, что-то пошло не так, как надо.

И что?

В Вашем случае необходимо выбрать:

Если Вам действительно нужен молниеносный доступ, то средство доступа броска могло быть проблемой. Но это означает, что Вы уже использовали профилировщика на своем коде к определенному, это - узкое место, не так ли?

;-)

Если Вы знаете, что не наличие значения может часто происходить, и/или Вы хотите, чтобы Ваш клиент распространил возможный null/invalid/whatever семантический указатель на значение, к которому получают доступ, затем возвратил указатель (если Ваше значение в простом указателе), или слабый/общий указатель (если Ваше значение принадлежит общему указателю).

Но если Вы полагаете, что клиент не распространит это "пустое" значение, или что они не должны распространять Нулевого указателя (или интеллектуальный указатель) в их коде, затем используйте ссылку, защищенную исключением. Добавьте "hasValue" метод, возвратив булевскую переменную и добавьте, что бросок должен пользователь пробовать получение значения, даже если нет ни одного.

Наконец, что не менее важно, рассмотрите код, который будет использоваться пользователем Вашего объекта:

// If you want your user to have this kind of code, then choose either
// pointer or smart pointer solution
void doSomething(MyClass & p_oMyClass)
{
   MyValue * pValue = p_oMyClass.getValue() ;

   if(pValue != NULL)
   {
      // Etc.
   }
}

MyValue * doSomethingElseAndReturnValue(MyClass & p_oMyClass)
{
   MyValue * pValue = p_oMyClass.getValue() ;

   if(pValue != NULL)
   {
      // Etc.
   }

   return pValue ;
}

// ==========================================================

// If you want your user to have this kind of code, then choose the
// throwing reference solution
void doSomething(MyClass & p_oMyClass)
{
   if(p_oMyClass.hasValue())
   {
      MyValue & oValue = p_oMyClass.getValue() ;
   }
}

Так, если Ваша основная проблема выбирает между двумя пользовательскими кодами выше, Ваша проблема не о производительности, но "эргономии кода". Таким образом решение для исключения не должно быть отложено из-за потенциальных проблем производительности.

:-)

2
ответ дан 2 December 2019 в 04:54
поделиться

Средство доступа?

Идея "итератора", предложенная передо мной, интересна, но основное назначение итераторов является навигацией через контейнер. Не как простое средство доступа.

Я соглашаюсь с paercebal, итератор должен выполнить итерации. Мне не нравится способ, которым делает STL. Но идея средства доступа кажется более привлекательной. Таким образом, в чем мы нуждаемся? Контейнер как класс, который чувствует себя подобно булевской переменной для тестирования, но ведет себя как исходный тип возврата. Это было бы выполнимо с операторами броска.

template <T> class Accessor {
    public:
        Accessor(): _value(NULL) 
        {}

        Accessor(T &value): _value(&value)
        {}

        operator T &() const
        {
            if (!_value)
               throw Exception("that is a problem and you made a mistake somewhere.");
            else
               return *_value;
        }

        operator bool () const
        {
            return _value != NULL;
        }

    private:
        T *_value;
};

Теперь, какая-либо обозримая проблема? Использование в качестве примера:

Accessor <type> value = list.get(key);

if (value) {
   type &v = value;

   v.doSomething();
}
1
ответ дан 2 December 2019 в 04:54
поделиться

(Я понимаю, что это - не всегда правильный ответ и мой немного сильный тон, но необходимо рассмотреть этот вопрос прежде, чем решить для других более сложных альтернатив):

Так, что случилось с возвратом указателя?

Я много раз видел этого в SQL, где люди сделают свое серьезное, чтобы никогда иметь дело со столбцами NULL, как у них есть некоторая заразная смерть или что-то. Вместо этого они умно придумывают "пробел" или "не там" искусственное значение как-1, 9999 или даже что-то как '@X-EMPTY-X'.

Мой ответ: язык уже имеет конструкцию для "не там"; идите вперед, не бойтесь использовать его.

1
ответ дан 2 December 2019 в 04:54
поделиться

то, что я предпочитаю делать в таких ситуациях, имеет бросок, "добираются" и для тех обстоятельств, где вопрос производительности или failiure распространены, имеют функцию "tryGet" вроде "bool tryGet (введите ключ, значение ** стр)", чей контракт - то, который, если верный еще возвращается затем *стр == допустимый указатель на некоторый объект *, стр являются пустыми.

0
ответ дан 2 December 2019 в 04:54
поделиться

@aradtke, Вы сказали.

Я соглашаюсь с paercebal, итератор должен выполнить итерации. Мне не нравится способ, которым делает STL. Но идея средства доступа кажется более привлекательной. Таким образом, в чем мы нуждаемся? Контейнер как класс, который чувствует себя подобно булевской переменной для тестирования, но ведет себя как исходный тип возврата. Это было бы выполнимо с операторами броска [..] Теперь, какая-либо обозримая проблема?

Во-первых, ВЫ НЕ ХОТИТЕ ОПЕРАТОРА bool. Посмотрите Безопасную идиому Bool для большего количества информации, Но о Вашем вопросе...

Вот проблема, пользователи должны теперь explict бросок в случаях. Указатель как прокси (такой как итераторы, ref-counted-ptrs, и необработанные указатели) имеет краткое, 'получают' синтаксис. Обеспечение оператора преобразования не очень полезно, если вызывающие стороны должны вызвать его с дополнительным кодом.

Начиная с Вашего перезабора как пример, самый краткий способ записать это:

// 'reference' style, check before use
if (Accessor<type> value = list.get(key)) {
   type &v = value;
   v.doSomething();
}
// or
if (Accessor<type> value = list.get(key)) {
   static_cast<type&>(value).doSomething();
}

Это хорошо, не понимайте меня превратно, но это является более подробным, чем это должно быть. теперь рассмотрите, знаем ли мы, по некоторым причинам, что list.get успешно выполнится. Затем:

// 'reference' style, skip check 
type &v = list.get(key);
v.doSomething();
// or
static_cast<type&>(list.get(key)).doSomething();

Теперь отпускает назад к поведению итератора/указателя:

// 'pointer' style, check before use
if (Accessor<type> value = list.get(key)) {
   value->doSomething();
}

// 'pointer' style, skip check 
list.get(key)->doSomething();

Оба довольно хороши, но синтаксис указателя/итератора просто немного короче. Вы могли дать 'ссылочный' стиль, который функция членства 'получает ()'..., но это уже, для чего оператор* () и оператор-> ().

Средство доступа стиля 'указателя' теперь имеет оператор 'неуказанный bool', оператор* и оператор->.

И угадайте то, что... необработанный указатель отвечает этим требованиям, таким образом, для разработки прототипа, list.get () возвращает T* вместо Средства доступа. Затем, когда дизайн списка стабилен, можно возвратиться и записать Средство доступа, подобный указателю тип Прокси.

0
ответ дан 2 December 2019 в 04:54
поделиться

Интересный вопрос. Это - проблема в C++ для эксклюзивного использования ссылок, которые я предполагаю - в Java, ссылки являются более гибкими и могут быть пустыми. Я не могу помнить - ли это легальный C++ для принуждения нулевой ссылки:

MyType *pObj = nullptr;
return *pObj

Но я считаю это опасным. Снова в Java я выдал бы исключение, поскольку это распространено там, но я редко вижу исключения, используемые так свободно в C++. Если я делал puclic API для допускающего повторное использование компонента C++ и должен был возвратить ссылку, я предполагаю, что пошел бы путем исключения. Мое реальное предпочтение состоит в том, чтобы иметь возврат API указатель; я считаю указатели неотъемлемой частью C++.

-6
ответ дан 2 December 2019 в 04:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: