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

Почему внешний temp становится пустым после отлова первого исключения?

#include <iostream>
int main()
{
    std::string temp("exception");
    int value;
    while(std::cin>> value && value != 0)
    {
         try{
              if(value > 9) throw temp;
              else std::cout << value << "\n";
            }
         catch(std::string temp)
         {
              std::cout << temp << "\n";
         }
    }

    return 0;
}

Входные данные:

1
2
11
13

Выход:

1
2
exception
// Printing Empty string

Ожидаемый результат:

1
2
exception
exception

Я компилирую свой код с помощью g++ 7.3.0.

Ответ 1

Похоже, что это ошибка в реализации GCC разрешения копирования. Стандарт C++ гласит следующее:

[class.copy.elision] (акцент мой)

Такое исключение операций копирования/перемещения, называемое разрешением копирования, допускается при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):

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

В следующих контекстах инициализации копирования вместо операции копирования может использоваться операция перемещения:

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

Это семейство оптимизаций, которое позволяет избежать инициализации копирования объекта исключения или сделать его максимально эффективно. Теперь общая реализация конструкции перемещения std::string - оставить исходную строку пустой. Похоже, это именно то, что происходит с вашим кодом. temp во внешней области видимости перемещается из (и остается пустым).

Но это не намеченное поведение. Объем создаваемого вами temp интервала превышает (на сегодняшний день) блок try, в который он был добавлен. Таким образом, GCC не имеет права применять к нему разрешение на копирование.

Возможный обходной путь заключается в размещении декларации temp внутри в while цикла. Это инициализирует новый объект std::string каждой итерации, поэтому даже если GCC удаляется от него, это не будет заметно.

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

Ответ 2

Я не уверен, является ли это ошибкой или нет, как упоминалось в другом ответе, но каким-то образом блок catch изменяет/пропускает содержимое temp после обработки исключения один раз. Ниже код решает эту проблему. Сделать temp const чтобы решить эту проблему.

#include <iostream>
    int main()
    {
        const std::string temp("exception");
        int value;
        while(std::cin>> value && value != 0)
        {
             try{
                  if(value > 9) throw temp;
                  else std::cout << value << "\n";
                }
             catch(std::string temp){
                  std::cerr << temp << "\n";
                  }
        }

        return 0;
    }