Киньте временную вместо локальной переменной - почему?

Код, о котором идет речь, такой:

struct something_bad_happened_exception : std::exception {};

void foo() {
    something_bad_happened_exception e;
    throw e;
}

Clang выдает предупреждение:

Выражение броска должно бросить анонимные временные значения вместо [Серт-err09-каст]

это означает, что foo() следует изменить на:

void foo() {
    throw something_bad_happened_exception();
}

Почему лучше бросать временную переменную вместо локальной переменной?

Ответы

Ответ 1

В соответствии со ссылкой cpp на выбрасываемое выражение:

Во-первых, copy-инициализирует объект исключения из выражения (это может вызвать конструктор перемещения для выражения rvalue, а для копирования/перемещения может быть выполнено исключение копирования)

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

При следующих обстоятельствах компиляторам разрешено, но не требуется опускать копию

[omissis]

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

В качестве примера рассмотрим следующий код

#include <exception>
#include <iostream>

struct something_bad_happened_exception : std::exception {
  something_bad_happened_exception(const something_bad_happened_exception& r) {
     std::cout << "A copy has occoured!" << std::endl;
  }
  something_bad_happened_exception() { }
};

int main()
{
  std::cout << "First throw" << std::endl;
  try {
    const something_bad_happened_exception e;
    throw e;
  }
  catch (const std::exception& ex)
    {
      std::cout << "Caught exception" << std::endl;
    }
  std::cout << "Second throw" << std::endl;
  try {
    throw something_bad_happened_exception();
  }
  catch (const std::exception& ex)
    {
      std::cout << "Caught exception" << std::endl;
    }

  return 0;
}

Скомпилируя код с помощью gcc 8.2.1 и clang 6.0, с опцией -O3, вы получите

First throw
A copy has occoured!
Caught exception
Second throw
Caught exception

Первый throw соответствует вашему примеру. Даже если копия e может быть опущена, ни gcc, ни clang не реализуют исключение копирования.

Второй throw имеет временный анонимный доступ, и копия не появляется.

Ответ 2

Код не соответствует соглашению "throw only anonymous temporaries" (обратите внимание на опцию CheckThrowTemporaries), которое проверяет средство проверки стиля.

Это соглашение и эта проверка являются слишком строгими в том, что касается соглашения "бросок по значению" - в этом отношении пример программы соответствует. Действительно несоответствующим примером будет бросать указатель на объект.