¿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?

Author: Dietmar Kühl, 2013-12-31

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!

 16
Author: Dietmar Kühl,
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

 13
Author: David Rodríguez - dribeas,
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;
 12
Author: Daniel Frey,
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