Рассмотрим следующий код:
int x;
int& f() {
return x ? x : throw 0;
}
С gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)
Я получаю следующую ошибку компиляции:
cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
Обратите внимание, что это прекрасно компилируется в Clang. Вот (что я считаю) соответствующее заявление из стандарта:
N4659 [8.16.2.1] (Условный оператор):
Второй или третий операнд (но не оба) является (возможно, заключенным в скобки) throw-выражением (8.17); результат имеет тип и значение категории другого.
Насколько я понимаю, x
это lvalue, так что мне кажется, что clang
правильно. Я ошибся?
Если бы мне пришлось сделать предположение, преобразование l-to-rvalue происходит потому, что два выражения в условном выражении не имеют одинаковый тип, а потому что второе — throw, это преобразование должно быть прервано. Я не знаком с отправкой отчетов об ошибках, но, возможно, это был бы лучший форум для этого.
Вот некоторые (возможно) более полезные вопросы об условном операторе:
Почему эта функция возвращает ссылку на lvalue с заданными аргументами rvalue?
Ошибка: lvalue требуется в этом простом C-коде? (Троичный с заданием?)
Clang здесь правильный, старое поведение было безоговорочно преобразовывать значение в prvalue который, похоже, все еще реализует gcc.
Это было предметом DR 1560 который был установлен резолюцией DR 1550. DR 1560 говорит:
Glvalue, появляющийся как один операнд условного выражения в
который другой операнд является бросающим выражением, преобразуется в
prvalue, независимо от того, как используется условное выражение:Если второй или третий операнд имеет тип void, то lvalue-to-rvalue (7.1 [conv.lval]), массив-указатель (7.2
[conv.array]) и стандартная функция-указатель (7.3 [conv.func])
преобразования выполняются на втором и третьем операндах, и один из
следующее должно иметь место:
- Второй или третий операнд (но не оба) является выражением броска (18.1 [кроме. Броска]); результат имеет тип
другой и является prvalue.Это кажется неожиданным и неожиданным.
а также DR 1550
изменил формулировку в [Expr.cond] к тому, что мы имеем сейчас:
Второй или третий операнд (но не оба) является (возможно, заключенным в скобки) throw-выражением; результат имеет тип и значение категории другого. Условное выражение является битовым полем, если этот операнд является битовым полем.
Похоже, что gcc реализует старое поведение, в то время как clang реализует DR.
Это патч, который применил DR 1560 для лязга. Добавлен следующий тест:
namespace DR1560 { // dr1560: 3.5
void f(bool b, int n) {
(b ? throw 0 : n) = (b ? n : throw 0) = 0;
}
class X { X(const X&); };
const X &get();
const X &x = true ? get() : throw 0;
}
который на Godbolt мы можем видеть, что это не удается для GCC из-за:
error: lvalue required as left operand of assignment
4 | (b ? throw 0 : n) = (b ? n : throw 0) = 0;
|
У нас есть gcc отчет об ошибке для очень похожей проблемы который имеет следующий сокращенный контрольный пример:
Я хотел устранить эту ошибку и предоставить более простой контрольный пример:
void blah(int&) {} int main() { int i{}; blah(true ? i : throw); }
результат с gcc 6.0:
prog.cc: In function 'int main()': prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' blah(true ? i : throw 0); ~~~~~^~~~~~~~~~~~~
Других решений пока нет …