c++referencervaluelvalue

C++ Conditional expression removes reference when other type is throw expression


I have a function that takes a reference to a T*: void f(T *&t);. When I call it with a conditional expression with a throw, f(t == nullptr ? throw "nullptr" : t), the program fails to compile:

error: cannot bind non-const lvalue reference of type 'T*&' to an rvalue of type 'T*'
note:   initializing argument 1 of 'f(T*&)'

Yet replacing the above call with f(t) compiles just fine.

Why is that? The whole expression should have the same type as the non-throw operand: https://en.cppreference.com/w/cpp/language/operator_other. From the link, Either E2 or E3 (but not both) is a (possibly parenthesized) throw-expression. The result of the conditional operator has the type and the value category of the other expression.

Reproducible example: https://godbolt.org/z/9MW1Kevxz

#include <iostream>
using namespace std;

struct T {
  int i;  
};

void f(T*& t) { 
    t->i = 2; 
}

int main()
{
    T *t = new T{5};
    f(t == nullptr ? throw "hi" : t);
    return 0;
}

Fails with gcc 9.4 on x86-64.


Solution

  • This is CWG 1550*. C++ standard used to require that the result of ternary operator is of type of the other (non-throw) operand, but the value category was always prvalue. That was changed eventually and the value category of the other operand is now kept, but it seems GCC only implemented the change in version 10 and higher (see it online in godbolt).

    *technically, the issue was reported in CWG 1560, but both were resolved under CWG 1550 and this is where the diff is shown.