Какой тип я должен поймать, если я брошу строковый литерал? - PullRequest
22 голосов
/ 28 января 2011

Я пишу довольно простое приложение на C ++, используя g ++ под Linux, и я пытаюсь использовать некоторые необработанные строки как исключения (да, я знаю, это не очень хорошая практика).

У меня есть следующий код (упрощенно):

int main()
{
  try
  {
    throw "not implemented";

  }
  catch(std::string &error)
  {
    cerr<<"Error: "<<error<<endl;
  }
  catch(char* error)
  {
    cerr<<"Error: "<<error<<endl;
  }
  catch(...)
  {
    cerr<<"Unknown error"<<endl;
  }
}

И я получаю Unknow error на консоли. Но если я приведу статическую строку к std::string или char *, она напечатает Error: not implemented, как и ожидалось. Мой вопрос: так какой тип я должен ловить, если я не хочу использовать статическое приведение?

Ответы [ 7 ]

30 голосов
/ 28 января 2011

Вам нужно поймать его с char const* вместо char*. Ни что-нибудь вроде std::string, ни char* не поймает это.

У ловли есть ограниченные правила относительно того, к каким типам он подходит. В спецификации сказано (где "cv" означает "комбинация const / volatile" или ни один из них).

Обработчик соответствует объекту исключения типа E, если

  • Обработчик имеет тип cv T или cv T &, а E и T относятся к одному и тому же типу (игнорируя квалификаторы cv верхнего уровня), или
  • обработчик имеет тип cv T или cv T &, а T является однозначным общедоступным базовым классом E, или
  • обработчик имеет тип cv1 T * cv2, а E является типом указателя, который может быть преобразован в тип обработчика любым из

    или обоими
    • стандартное преобразование указателя (4.10), не включающее преобразование указателей в закрытые или защищенные или неоднозначные классы
    • преобразование квалификации

Строковый литерал имеет тип char const[N], но выброс массива приведет к его разрушению и на самом деле вызовет указатель на его первый элемент. Таким образом, вы не можете поймать брошенный строковый литерал на char*, потому что в то время, когда он совпадает, ему нужно сопоставить char* с char const*, что отбрасывает const (преобразование квалификации допускается только для добавить const). Специальное преобразование строкового литерала в char* рассматривается только тогда, когда вам нужно специально преобразовать строковый литерал.

10 голосов
/ 28 января 2011

Попробуйте добавить const к типам, которые вы ловите, const char* (возможно, const char* const).

4 голосов
/ 28 января 2011

Точный тип строкового литерала - это массив константных символов (const char [15] для вашего примера, поскольку включен терминатор NUL). Массив уменьшается до const char* при выдаче, что не зависит от длины.

1 голос
/ 28 января 2011

Ознакомьтесь с разделом 2.14.5 стандартной спецификации, он рассматривает типы и типы строковых литералов на 3 страницах. Не делай того, что начал, просто скажи:

throw std::exception("not implemented");

вместе с собственно

catch (std::exception& pEx)

Что-то не так с этим "нормальным" подходом ...?

1 голос
/ 28 января 2011

Тип строкового литерала - char const *.Существует (не рекомендуется) преобразование в char * для обратной совместимости с существующим кодом (но вы все равно должны рассматривать его как const - любая попытка изменения дает UB).

Таким образом, код подобенэто должно работать:

#include <iostream>
using namespace std;

int main()
{
  try
  {
    throw "not implemented";

  }
  catch(char const *error)
  {
    cerr<<"Error: "<<error<<endl;
  }
  return 0;
}
1 голос
/ 28 января 2011

Проблема в том, что вы пытаетесь поймать что-то, что является постоянным. Будет работать следующее:

catch (const char * error) { сегг
1 голос
/ 28 января 2011

Тип должен быть const char[15] или const char*.

Однако, хотя язык не запрещает вам бросать любое значение типа, вы не должны вызывать собственные типы данных в качестве исключения. Вместо этого вы хотите создать экземпляр std::exception() или создать собственный класс исключений.

...