98

I have a reference to some class MyObject, but the exact object depends on a condition. I want to do something like this:

MyObject& ref; 
if([condition]) 
  ref = MyObject([something]);
else 
  ref = MyObject([something else]);

I cannot do this right now because the compiler does not allow me to declare but not initialize a reference. What can I do to achieve my goal here?

RespiteSage
  • 23
  • 1
  • 6
user1861088
  • 1,683
  • 2
  • 15
  • 17
  • 3
    Would that be an initialization from a temporary? That won't work even without the condition: `MyObject& ref = MyObject([something]);`, because you cannot bind a temporary to a non-const lvalue reference. – GManNickG Feb 14 '13 at 20:02
  • @GManNickG : this apply to Zaffy and suszterpatt answers too? – qPCR4vir Feb 14 '13 at 20:18
  • @qPCR4vir: Yup. The question still stands, in a way, just not directly. – GManNickG Feb 14 '13 at 20:21
  • 1
    If you are searching for an actual solution, scroll down to https://stackoverflow.com/a/50909452/1021920 – hellow Feb 28 '20 at 10:47
  • This question and the answer by @Latawiec below involving an immediately invoked lambda expression is very useful for initialization of references and constants based on complicated expressions. – Hari May 07 '23 at 17:36

13 Answers13

66

You need to initliaze it. But if you would like to conditionally initialize it, you can do something like this:

MyObject& ref = (condition) ? MyObject([something]) : MyObject([something else]);
Zaffy
  • 16,801
  • 8
  • 50
  • 77
  • 12
    I can't do this because I actually do stuff inside those conditions. I'm very confuse here. This looks like a pretty common scenario to me why isn't it allowed? Is my coding style that anti-c++ ? – user1861088 Feb 14 '13 at 20:19
  • 4
    @user1861088: "I can't do this because I actually do stuff inside those conditions" So why don't you actually show what you're trying to do in the question? – GManNickG Feb 14 '13 at 20:20
  • 1
    what i meant to say is I execute multiple lines of code inside each condition instead of just calling one function that returns a value. So I can't use ? : right? – user1861088 Feb 14 '13 at 20:24
  • 1
    @user1861088 couldnt you initialize the reference, then test the condition again and execute them multiple lines of code? References cannot be changed once created. References can never be null. Those two things keep you from doing what you posted in the question. – iheanyi Jul 24 '14 at 20:36
  • 9
    You could consider to use a lambda. – sergiol Nov 12 '15 at 13:58
  • @user1861088 sometimes instead of the semicolon you can use the comma operator—(expr1,expr2) evaluates both but only the second one becomes the final value. This _usually_ creates horrible unreadable code but I think should be mentioned to make this answer complete. – Artelius Nov 26 '19 at 03:39
  • Then if (...){r=A;...} else {r=B;} need to be written as r=(...)?A:B; if (...){...}. C++ - it just doesn't work and you always need to waste your time to get it work. – jw_ Feb 22 '20 at 07:39
29

AFAIK this can't be done with a reference. You'd have to use a pointer:

MyClass *ptr;

if (condition)
    ptr = &object;
else
    ptr = &other_object;

The pointer will act similar to a reference. Just don't forget to use -> for member access.

David G
  • 94,763
  • 41
  • 167
  • 253
  • 8
    You can just declare a reference as `MyClass &ref = *ptr` if you want to turn it into a reference... – poizan42 Aug 26 '16 at 11:20
  • declaring it as a pointer won't solve the case of rvalue being the return of a function – Mystic Odin Jan 18 '17 at 13:41
  • No, It *will not* act similar to a reference since it will be placed on the heap and it will require manual deletion. – Josu Goñi Oct 17 '19 at 08:01
  • 2
    @JosuGoñi Neither is true. Only if I were to allocate memory (via `new`). `ptr` points to objects allocated on the stack. – David G Oct 17 '19 at 21:50
  • @0x499602D2 He has to use new unless he can use a move constructor. – Josu Goñi Oct 18 '19 at 05:25
  • 1
    @JosuGoñi Assigning to a pointer will not cause a copy or a move. Though I admit my answer isn't the best, it was written back when I knew next to nothing. Today I'd say to make `ptr` into a value type and assign to values. If it cannot be default-initialized then I would say to use a `std::unique_ptr` and assign using `std::make_unique(...)` within each condition. – David G Oct 18 '19 at 19:29
  • This will not work if "object" or "other_object" are object-definition using constructors as you are creating a pointer to a temporary object! The compilers will give an error. As a workaround, you may use shared pointers. Check my answer below. – M. Khaled Nov 01 '20 at 17:41
  • True. See here for more info: https://en.cppreference.com/w/cpp/language/reference_initialization – Miguel Tomás Mar 16 '23 at 13:24
21

What I like to do is a lambda that's immediately executed.

Let's suppose we want a const std::string& to a variable from under the map - if map does not contain given key - we want to throw.

int main()
{
  std::map<std::string, std::string> myMap = {{"key", "value"}};

  const std::string& strRef = [&]()->const std::string& {
    try {
      return myMap.at("key"); // map::at might throw out_of_range
    }
    catch (...) {
      // handle it somehow and/or rethrow.
    }
  }(); // <- here we immediately call just created lambda.
}

You could also use std::invoke() to make it more readable (since C++17)

int main()
{
  std::map<std::string, std::string> myMap = {{"key", "value"}};

  const std::string& strRef = std::invoke([&]()->const std::string& {
    try {
      return myMap.at("key"); // map::at might throw out_of_range
    }
    catch (...) {
      // handle it somehow and/or rethrow.
    }
  });
}
Latawiec
  • 341
  • 2
  • 10
  • 4
    yes. if only ppl would answer a question rather than preferring to suggest the question should not be asked. this is probably the cleanest on-the-fly method that actually addresses the OP. +1. folk should scroll down more often.. lol – violet313 Feb 22 '19 at 19:45
  • Very useful answer. See here for more such usage: https://www.cppstories.com/2016/11/iife-for-complex-initialization/ – Hari May 07 '23 at 17:42
18

You can't do this. References must be bound to something, you may not like it but it prevents a whole class of errors, because if you have a reference you can always assume it's bound to something, unlike a pointer which could be null.

Your example code wouldn't work anyway because you attempt to bind a non-const reference to a temporary object, which is invalid.

Why do you need it to be a reference anyway? One solution would be to ensure your type has an inexpensive default constructor and can be efficiently moved, then just do:

MyObject obj; 
if([condition]) 
  obj = MyObject([something]) 
else 
  obj = MyObject([something else]);

Otherwise you'd have to put the conditional code in one or more functions, either:

const MyObject& ref = createObject([condition]);

or

const MyObject& ref = [condition] ? doSomething() : doSomethingElse();

Note that both these versions use a const reference, which can bind to a temporary, if the object must be non-const, then again stop trying to use a reference:

MyObject obj = createObject([condition]);

This will probably be just as efficient as what you were trying to do, thanks to the return value optimization

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
13

In C++, you can't declare a reference without initialization. You must initialize it.

12

Short answer: you don't.

Marginally longer answer: do something like this:

MyObject& getObject()
{
    if([condition]) 
        return [something] 
    else 
        return [something else];
}

MyObject& ref = getObject();

Usual disclaimers regarding references apply of course.

suszterpatt
  • 8,187
  • 39
  • 60
  • 1
    yeah i thought of this but.. it is just weird that i have to do all this to achieve a seemingly simple goal :( – user1861088 Feb 14 '13 at 20:20
  • Why does `getObject()` return a reference? what does it refer to? – Jonathan Wakely Feb 14 '13 at 20:25
  • &Jonathan: That falls under the "usual disclaimers" bit. ;) – suszterpatt Feb 14 '13 at 21:23
  • 1
    @user1861088 You seemingly simple goal is not allowed by the standard. It conflicts with the definition of a reference. You might as well ask why you can't simply toss a rock in a air and have it not fall to the ground. That's simple right - just toss the rock and it stays in place. – iheanyi Jul 24 '14 at 20:38
4
MyClass *ptr;

if (condition)
    ptr = &object;
else
    ptr = &other_object;

MyClass &ref = *ptr;
Anonymous
  • 75
  • 1
  • 1
  • 4
    When answering, consider writing an explanation of the code you wrote, and this answer adds absolutely nothing new to the *very* old question. – Ajean Mar 27 '15 at 23:05
  • 1
    This looks very very similar to the code from [this answer](http://stackoverflow.com/a/14885051). – Artjom B. Mar 27 '15 at 23:21
  • While the lack for any explanation is bad, this is still the only real answer that doesn't introduce anything extra or limits you to what can be written in a ternary operator... – poizan42 Aug 26 '16 at 11:21
  • 1
    @ArtjomB. that answer does not propose the `MyClass &ref = *ptr;` at the end, which is the key line here that avoid further dereferences later on, further commented at: https://stackoverflow.com/a/62793754/895245 – Ciro Santilli OurBigBook.com Jul 08 '20 at 11:28
2

use a static dummy as place holder. or std::optional with reference_wrapper.

static X placeHolder_;
std::reference_wrapper<X> ref = placeHolder_;
   


std::optional<std::reference_wrapper<X>> ref ;    
alhazeem
  • 21
  • 2
0

I usually do this (C++ 11 or later):

std::shared_ptr<ObjType> pObj;
if(condition)
    pObj = std::make_shared<ObjType>(args_to_constructor_1);
else
    pObj = std::make_shared<ObjType>(args_to_constructor_2);

which is clean and allows using object definition with (possibly different) constructions, a thing you can't do directly with pointers as the compiler will complain from using temporary objects.

M. Khaled
  • 119
  • 1
  • 2
0

I had a similar question, I solved it in a not so smart way. Question: I need declare a variable, its type is determined by element of "data"; the variable will be used outside the first if-condition, so it needs to be initialized outside the first if-condition.

wrong code:

Ivar& ref; // invalid, need init
if([condition]){ 
  ref = data["info"];
  if (ref.is_dict()) {
     ...
  }
}
string x = ref["aa"]; 


  

Use a temp variable in condition

Ivar& ref = dict(); // Ivar and dict are faked; Ivar is interface variable 
if([condition]){ 
  Ivar& ref_tmp = data["info"];
  if (ref_tmp.is_dict()) { // if the type of temp variable is correct
     ref = ref_tmp  // assign ref_tmp to ref
     ...
  }
}
string x = ref["aa"]; 
JOE yue
  • 1
  • 2
-1

if([condition]) MyObject& ref = MyObject([something]); else MyObject& ref= MyObject([something else]);

VIPK
  • 9
  • 1
-2

You can use a template which does not require initialization until you actually use it.

  template <uint8_t c>
  uint8_t& ref; 
void setup()
{
  uint8_t a=1;
  uint8_t b=2;
if(true) 
  ref<1> = a;
else 
  ref<1> = b;
}

Some old IDEs may mark it as an error but it will finely pass the compilation.

-3

You can use "extern" keyword: first time (assume, in header file) you can declare your variable preceding declaration with "extern" keyword. Later (in source file) you repeat declaration without "extern" and assign value to it.

Kirill Frolov
  • 401
  • 4
  • 10