¿Se puede lanzar una excepción desde el operador ternario?
A veces es conveniente o incluso necesario tener una función que solo una declaración (es necesario cuando se devuelve un constexpr
). Si es necesario verificar una condición y solo se permite una instrucción, el operador condicional es la única opción. En caso de un error serÃa bueno lanzar una excepción del operador condicional, por ejemplo:
template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
return it == end? throw std::runtime_error("no element"): *it;
}
La función anterior no compila, sin embargo, cuando se usa por ejemplo como ( ejemplo vivo):
std::vector<int> v;
access(v.begin(), v.end());
El compilador se queja de intentar vincular una referencia no-const
a una temporal. Sin embargo, el compilador no se queja de la expresión throw
per se. Asà que la pregunta: ¿Se pueden lanzar excepciones desde el operador condicional y, si es asÃ, qué va mal con el código anterior?
3 answers
El operador condicional se describe en 5.16 [expr.cond]. Su párrafo 2 incluye el texto siguiente:
El segundo o tercer operando (pero no ambos) es una expresión de lanzamiento (15.1); el resultado es del tipo del otro y es un prvalue.
Que dice que se permite lanzar una excepción del operador condicional. Sin embargo, incluso si la otra rama es un lvalue, se convierte en un rvalue! Por lo tanto, no es posible vincular un lvalue a la resultado de una expresión condicional. Aparte de reescribir la condición usando el operador coma, el código podrÃa reescribirse para obtener solo el lvalue del resultado del operador condicional:
template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
return *(it == end? throw std::runtime_error("no element"): it);
}
El asunto algo complicado es que devolver una referencia const
de la función compilarÃa pero en realidad devolverÃa una referencia a un temporal!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-30 22:00:38
El texto en el estándar es alrededor de 5.16 / 2:
Si el segundo o el tercer operando tiene el tipo void, entonces las conversiones estándar lvalue-to-rvalue (4.1), array-to-pointer (4.2) y function-to-pointer (4.3) se realizan en el segundo y tercer operandos, y uno de los siguientes se llevará a cabo:
- El segundo o tercer operando (pero no ambos) es una expresión throw (15.1); el resultado es del tipo del otro y es un prvalue.
Que explica el comportamiento que estás teniendo. Es legal lanzar, pero el tipo de la expresión es un pure-rvalue (incluso si la expresión es un lvalue ) y por lo tanto no se puede vincular una no-const lvalue-reference
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-30 21:58:59
Se puede hacer asÃ:
return it == end? (throw std::runtime_error("no element"),*it): *it;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-30 21:55:42