На самом деле обработка исключений имеет специальные правила в отношении lvalue и rvalue. Объект временного исключения представляет собой lvalue, см. 15.1/3 текущего черновика:
Выражение throw инициализирует временный объект, называемый объектом исключения, тип которого определяется путем удаления любых cv-квалификаторов верхнего уровня из статического типа операнда throw и настройки типа из «массива T» или « функция, возвращающая T» в «указатель на T» или «указатель на функцию, возвращающую T» соответственно. Временное значение представляет собой lvalue и используется для инициализации переменной, названной в соответствующем обработчике (15.3). Если тип объекта исключения будет неполным типом или указателем на неполный тип, отличный от void (возможно, cv-qualified), программа будет неправильно сформирована. За исключением этих ограничений и ограничений на сопоставление типов, упомянутых в 15.3, операнд throw обрабатывается точно так же, как аргумент функции в вызове (5.2.2) или операнд оператора return.
И захват по ссылке rvalue тоже незаконен, см. 15.3/1:
Объявление исключения в обработчике описывает тип(ы) исключений, которые могут привести к входу в этот обработчик. Объявление исключения не должно обозначать неполный тип или ссылочный тип rvalue. Объявление исключения не должно обозначать указатель или ссылку на неполный тип, кроме void*, const void*, volatile void* или const volatile void*.
Кроме того, вы, кажется, не понимаете идеальную переадресацию. Ваш прямой призыв не лучше, чем ход. Идея идеальной переадресации состоит в том, чтобы закодировать категорию значения аргумента как часть типа и позволить выводу аргумента шаблона выяснить это. Но ваш обработчик исключений не является и не может быть шаблоном функции.
По сути, идеальная переадресация основана на выводе аргументов шаблона и ссылках на rvalue:
void inner(const int&); // #1 takes only lvalues or const rvalues
void inner(int&&); // #2 takes non-const rvalues only
template<class T>
void outer(T && x) {
inner(forward<T>(x));
}
int main() {
int k = 23;
outer(k); // outer<T=int&> --> forward<int&> --> #1
outer(k+2); // outer<T=int> --> forward<int> --> #2
}
В зависимости от категории значения аргумента дедукция аргумента шаблона выводит T либо как ссылку на lvalue, либо как нормальный тип значения. Из-за свертывания ссылок T&& также является ссылкой lvalue в первом случае или ссылкой rvalue во втором случае. Если вы видите, что T&& и T - это параметр шаблона, который можно вывести, это в основном "поймать все". std::forward восстанавливает исходную категорию значений (закодированную в T), поэтому мы можем идеально перенаправить аргумент в перегруженные внутренние функции и выбрать правильный. Но это работает только потому, что external является шаблоном и существуют специальные правила для определения T по отношению к его категории значений. Если вы используете ссылки на rvalue без вывода аргументов шаблона/шаблона (как в # 2), функция будет принимать только rvalue.
person
sellibitze
schedule
05.10.2010