Подтвердить что ты не робот

Можно ли исключить исключение из тернарного оператора?

Иногда удобно или даже необходимо иметь функцию, которая имеет только один оператор (это необходимо при возврате constexpr). Если необходимо проверить условие и разрешить только один оператор, единственным условием является условный оператор. В случае ошибки было бы неплохо выбросить исключение из условного оператора, например:

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return it == end? throw std::runtime_error("no element"): *it;
}

Вышеуказанная функция не компилируется, однако, когда используется, например, как (живой пример):

std::vector<int> v;
access(v.begin(), v.end());

Компилятор жалуется на попытку привязки ссылки const к временному. Компилятор, однако, не жалуется на throw -expression. Итак, вопрос: можно ли исключить исключения из условного оператора, и если да, то что происходит с вышеуказанным кодом?

4b9b3361

Ответ 1

Условный оператор описан в 5.16 [expr.cond]. В его пункте 2 содержится следующий текст:

Второй или третий операнд (но не оба) является выражением throw (15.1); результат будет другого типа и является prvalue.

Это говорит о том, что разрешено исключать исключение из условного оператора. Однако, даже если другая ветвь является lvalue, она превращается в rvalue! Таким образом, невозможно связать lvalue с результатом условного выражения. Помимо перезаписи условия с использованием оператора запятой, код можно переписать, чтобы получить только lvalue из результата условного оператора:

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return *(it == end? throw std::runtime_error("no element"): it);
}

Несколько сложным делом является то, что возвращение ссылки const из функции будет компилироваться, но на самом деле возвращает ссылку на временную!

Ответ 2

Формулировка в стандарте составляет около 5.16/2:

Если либо второй, либо третий операнд имеет тип void, тогда стандартные преобразования конверсий lvalue-to-rvalue (4.1), array-to-pointer (4.2) и function-to-pointer (4.3) выполняются на второго и третьего операндов, и одно из следующих значений:

- второй или третий операнд (но не оба) является выражением throw (15.1); результат будет другого типа и является prvalue.

Это объясняет поведение, которое вы получаете. Легально бросать, но тип выражения является чисто-rvalue (даже если выражение является lvalue), и вы не можете таким образом связать не const const lvalue-reference

Ответ 3

Это можно сделать следующим образом:

return it == end? (throw std::runtime_error("no element"),*it): *it;