Я заметил небольшое несоответствие в том, как MSVC и g++ обрабатывают создание временного объекта исключения, когда брошенный тип является подвижным. Охота на них подняла дополнительные вопросы.
Прежде чем идти дальше, вот в чем суть моего вопроса: в отсутствие копирования/перемещения elision, кто хорошо делает стандарт, скажите, как создать объект временного исключения? На данный момент лучшим, что я смог сделать, является следующая цитата: от 15.1/3:
Выражение-выражение инициализирует временный объект, называемый объектом исключения, тип которого определяется удалением всех cv-квалификаторов верхнего уровня из статического типа операнда броска и корректировки типа из массива T "или" функция возвращает T "в" указатель на T "или" указатель на функцию возврата T "соответственно.
Я предполагаю, что ответ похоронен где-то на другом языке, который определяет тип выражения и как инициализируются объекты, но мне не повезло собрать все это вместе. Когда объект генерируется, создается ли объект исключения get (a), (b) перемещается, если это необходимо, и копируется в противном случае или (c) инициализируется в реализации определенным образом?
Рассмотрим следующий код:
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
struct Blob {
Blob() { cout << "C" << endl; }
Blob(const Blob&) { cout << "c" << endl; }
Blob(Blob&&) { cout << "m" << endl; }
Blob& operator =(const Blob&) { cout << "=" << endl; return *this; }
Blob& operator =(Blob&&) { cout << "=m" << endl; return *this; }
~Blob() { cout << "~" << endl; }
int i;
};
int main() {
try {
cout << "Throw directly: " << endl;
throw Blob();
} catch(const Blob& e) { cout << "caught: " << &e << endl; }
try {
cout << "Throw with object about to die anyhow" << endl;
Blob b;
throw b;
} catch(const Blob& e) { cout << "caught: " << &e << endl; }
{
cout << "Throw with object not about to die anyhow (enter non-zero integer)" << endl;
Blob b;
int tmp;
cin >> tmp; //Just trying to keep optimizers from removing dead code
try {
if(tmp) throw b;
cout << "Test is worthless if you enter '0' silly" << endl;
} catch(const Blob& e) { cout << "caught: " << &e << endl; }
b.i = tmp;
cout << b.i << endl;
}
}
Это все воссоздано на ideone. Как вы можете [надеяться] видеть, gcc через ideone создает объект Blob
на месте в первом случае и перемещается во втором. Результаты суммируются ниже, при этом значения указателя заменяются идентификаторами.
Throw directly:
C {A}
caught: {A}
~ {A}
Throw with object about to die anyhow
C {A}
m {B} <- {A}
~ {A}
caught: {B}
~ {B}
Throw with object not about to die anyhow (enter non-zero integer)
C {A}
m {B} <- {A}
caught: {B}
~ {B}
2
~ {A}
Тот же код в MSVC2010, независимо от настроек оптимизации, результаты те же, за исключением двух перемещений - это копии. В этом и заключается разница, которая изначально попалась мне на глаза.
Первый тест, который я предполагаю, хорош; его классическое копирование.
Во втором тесте gcc ведет себя так, как я ожидал. Временной Blob
обрабатывается как значение x, а объект исключения - это перемещение, построенное из него. Но я не уверен, что компилятор должен признать, что исходный Blob
истекает; если это не так, то MSVC действует правильно при копировании. Таким образом, мой первоначальный вопрос: соответствует ли стандартный мандат тому, что здесь происходит, или это просто часть реализации, определенного для обработки исключений?
Третий тест в точности противоположный: MSVC ведет себя так, как того требует моя интуиция. gcc хочет перейти от b
, но b
все еще жив, о чем свидетельствует тот факт, что я продолжаю работать с ним после обработки выброшенного исключения. Очевидно, что в этом тривиальном примере перемещение или копирование не имеет никакого значения для самого b
, но, конечно, компилятору не разрешено рассматривать это при рассмотрении разрешения перегрузки.
Очевидно, что наличие экземпляра copy/move elision делает этот простой тест трудным для обобщения, но большая проблема заключается в том, что либо компилятор может быть не совсем удовлетворительным еще [особенно в случае gcc с третьим тестом и MSVC в целом],
Обратите внимание, что это целиком для академических целей; Я почти никогда не бросаю ничего, кроме временного, которое оба компилятора создают на месте, и я вполне уверен, что поведение разрешено.