12

The following C++ is invalid because reference variables require initializers:

int& a; // illegal
if (isfive) {
  a = 5;
} else {
  a = 4;
}

However, MSVC seems to think this is okay:

int& a = isfive ? 5 : 4;

This implies to me that MSVC is actually treating the conditional operator like a single expression and not expanding it into an if-else statement.

Is it always valid C++ to initialize a reference using the conditional operator?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Kai
  • 2,482
  • 1
  • 19
  • 21

7 Answers7

15

The ternary operator does not expand to an if-else construct (not according to the language, the implementation might generate equivalent binaries, but at the language level they are different). So the following code is valid:

int four = 4, five = 5;
int& r = condition? four : five;

The original example in the question depends on a Microsoft extension that (incorrectly) allows binding a non-const reference to an rvalue expression.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
5

MSVC has a non-standard "extension". What it means is that it allows broken code. There's a good reason this is prohibited.

Note also that

int& a = 5;

is not legal in Standard C++ either.

In general, though, it is legal to initialize a const reference with any expression which can be converted to the right type (including use of the conditional operator). And it is legal to initialize a non-const reference with an lvalue of the right type, which the conditional operator yields under certain conditions.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 2
    Well what do you know, asking this question solved a problem I didn't even know I had. Thanks! – Kai Feb 08 '12 at 19:43
5

The code you posted does not compile with VC++ 2010:

Error 1 error C2440: 'initializing' : cannot convert from 'int' to 'int &'

Changing the line to:

const int& a = isfive ? 5 : 4; 

makes it compile.

Nemanja Trifunovic
  • 24,346
  • 3
  • 50
  • 88
3

The conditional operator is an expression, not a statement. It is perfectly fine to initialise a reference like that. It's a little like initialising a reference by calling a function.

Note that your reference needs to be const if you bind it to temporaries (a rule which MSVC++ stupidly ignores).

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • *It is prefectly fine to initialise a reference like that* only if we ignore the fact that you cannot bind a non-const reference to an rvalue... (i.e. `const int& r = isfive? 4 : 5;` is fine, but `int& r = isfive? 4 : 5;` is not) – David Rodríguez - dribeas Feb 08 '12 at 19:34
  • @DavidRodríguez-dribeas yeah, I added a note about that a while ago. – Seth Carnegie Feb 08 '12 at 19:35
1

it is not OK

int& a = isfive ? 5 : 4;

unless you declare the reference "a" as a const.

Dmitriy Kachko
  • 2,804
  • 1
  • 19
  • 21
0

Pointer + reference technique

It does feel like a limitation of the C++ language that could be overcome with a new language feature.

The most efficient option I can see until then (in case you are doing more in the if else than just setting the reference, in which case you could use the ternary operator ?) is to use a pointer, and then set the reference to the pointer to avoid doing multiple dereferences when using the variable later on when every time you use the variable:

main.cpp

#include <iostream>

int main(int argc, char **argv) {
    (void)argv;
    int x = 1;
    int y = 2;
    int *ptr;
    if (argc > 1) {
        ptr = &x;
    } else {
        ptr = &y;
    }
    // One pointer dereference here, I don't see how to get rid of this.
    int &z = *ptr;
    // No pointer dereference on any of the below usages.
    z = 3;
    std::cout << x << std::endl;
    std::cout << y << std::endl;
    std::cout << z << std::endl;
}

Compile:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp

run:

./main.out

output:

1
3
3

run again:

./main.out a

output:

3
2
3

If the if condition is known at compile time however, you could of course use the preprocessor or better, C++17 if constexpr: if / else at compile time in C++?

Related: Declare a reference and initialize later?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • You can get rid of pointer/dereferencing by lambda: `auto& ref = [&]() -> int& { if (cond) return x; return y;}();` – Quest Nov 16 '22 at 12:10
0

It is operator, part of the expression, not a statement. And you can't leave reference uninitialized even for a short while ;-)

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173