2013-03-09 3 views
6

C++ Standard заявляет следующее о выполнении std::call_once с функциями, которые бросают исключение (§30.4.4.2/2):Бросив исключение из станда :: call_once

2/Эффекты: выполнение call_once который не вызывает его func, является пассивным исполнением. Выполнение call_once, вызывающего его func, является активным исполнением. Активное выполнение должно вызвать INVOKE (DECAY_- COPY (std :: forward (func)), DECAY_COPY (std :: forward (args)) ...). Если такой вызов функции func вызывает исключение, выполнение является исключительным, в противном случае оно возвращается. Исключительное исполнение должно распространять исключение на вызывающего абонента call_once. Среди всех исполнений call_once для любого заданного значения once_flag: самое большее должно быть возвратное исполнение; если есть возвращаемое исполнение, это будет последнее активное выполнение; и существуют пассивные казни только в случае выполнения возвращаемого исполнения. [Примечание: пассивное выполнение позволяет другим потокам надежно наблюдать результаты, полученные в результате более раннего возвращения. - конец примечание]

Я использую Visual Studio 2012 и работаю следующий код:

void f(){ 
    throw std::exception("Catch me!"); 
} 

int main(int argc, char* argv[]){ 
    once_flag flag; 
    try{ 
     call_once(flag, f); 
    } catch(const std::exception& e){ 
     cout << e.what() << endl; 
    } 
    return 0; 
} 

Мой результат: код в прогонах поймать блок и выводит сообщение, но когда программа существую я получаю вызов abort() и следующее сообщение, выводимое COUT:

... \ mutex.c (38) мьютекса разрушен во время занят

Это должно произойти?

+2

Нет, это ошибка, программа должна работать нормально (хотя вы используете нестандартный конструктор для 'std :: exception', в ISO C++ вы можете использовать только конструкцию по умолчанию' std :: exception', что вызывает частые проблемы с переносимостью, когда пользователи MSVC проверяют incode, предназначенные для компиляции в других реализациях) –

ответ

7

Должно ли это случиться?

Нет, не совсем. Это ошибка .

Однако, обратите внимание на тот факт, что VC11 не одинок на этом:

  • Intel ICC 13.0.1 называет std::terminate() как если ваше исключение не было обработано (см live example);
  • GCC 4.8.0 beta, вероятно, делает что-то подобное, но не показывает никакого вывода, он просто проглатывает исключение и молча завершает программу (см. live example). [UPDATE: Это ошибка, кажется, не воспроизводимы в других окружающих средах, и, вероятно, будет проблема с конфигурацией на liveworkspace.org только]

GCC 4.7.2 и Clang 3.2, с другой стороны , ведут себя правильно.

Кстати, стоит заметить, что стандарт C++ (пункт 18.8.1) указывает, что std::exceptiononly has a default constructor and a copy constructor. Используемый вами конструктор скорее всего является не переносным расширением MS.

Вместо этого вы можете использовать std::logic_error, который происходит от std::exception и поддерживает конструктор, принимающий строку.

+1

Спасибо, я не знал, что std :: exception имеет только конструктор по умолчанию и копии, я фактически использую пользовательские исключения в исходном коде. На данный момент я воздержусь от исключения исключений из call_once, спасибо за разъяснение. –

+4

Также стоит отметить, что когда call_once возвращает с исключением, он не считается выполненным. То есть в следующий раз, когда 'call_once' будет выполнен,' f' будет снова предпринята попытка. И так далее, пока 'f' не будет выбрасывать исключение. –

+0

@HowardHinnant: Хорошее наблюдение, спасибо, что упомянули об этом. –