Бросок «исключения» и продолжение «возврата нормального значения» противоречат.
Как вы уже писали, вы можете помнить, что проблема произошла в некотором члене SomeObject или в некоторой переменной, которая передается как ссылка на конструктор.
Чтобы запретить вызывающей стороне не обрабатывать ошибку, вы можете создать «сигнальный элемент» таким образом, чтобы, если вызывающая сторона не опрашивала его, вы утверждали в деструкторе SomeObject (или в деструкторе сигнального элемента).
Возможный пример сигнального элемента:
template <typename Base>
class Signal
{
private:
Base _val;
bool _polled;
public:
operator Base() const {_polled=true;return _val;}
// even when we do not set the value, we force the user to poll - it makes testing easier
Signal():_polled(false) {}
// but once we set it, we mark he has to poll
Signal(Base val):_val(val),_polled(false) {}
// if he does not, we assert
~Signal(){assert(_polled);}
};
Если вы установите значение, и вызывающая сторона не «получит» его, деструктор будет утверждать. Если вызывающий получит его, но все равно проигнорирует, то вам не повезло, но то же самое относится и к исключениям: если вызывающий перехватывает его и игнорирует, вы ничего не можете с этим поделать. Вот как бы вы его использовали:
SomeObject Foo(int input, Signal<int> &errorCode)
{
SomeObject result;
// do something.
if (somethingBadHappened)
errorCode = 1234; //oh, we need to warn the caller.
return result;
}
void Main()
{
SomeObject object;
Signal<int> error;
object = Foo(1,error);
// error not served? => assert
// to prevent the assert firing, uncomment the following line
// int handle = error;
}
Примечание: вместо assert вы также можете вызвать исключение в деструкторе Signal. Имейте в виду, однако, что метание от деструкторов трудно сделать правильно - вы должны быть особенно осторожны в том, как именно вы используете класс Signal (и что будет использоваться в качестве базы для него).