Simultaneously create variable and pass as reference

May 28, 2022 at 11:49am
Is it possible to create a variable and simultaneously pass it as reference to another function?

Say I have a function:

1
2
3
4
  void HandleEvent(Event& e)
  {
    // do stuff
  }


The Event-constructor takes no parameters.
I can do:
1
2
  Event e;
  HandleEvent(e);


I'd like to be able to do something like:

 
  HandleEvent(Event());
Last edited on May 28, 2022 at 11:50am
May 28, 2022 at 12:06pm
You can have const references:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Event {};

void HandleEvent(Event&)
{
}

void Handle(const Event&)
{
}

int main()
{
  Event e;
  HandleEvent(e);
  HandleEvent( Event() ); // error
  Handle( Event() );
}

 In function 'int main()':
15:24: error: invalid initialization of non-const reference of type 'Event&' from an rvalue of type 'Event'
3:6: note: in passing argument 1 of 'void HandleEvent(Event&)'
May 28, 2022 at 12:20pm
You can't pass an r-value (temp) as a parameter expecting a ref. This either has to be const ref or passed by value. In HandleEvent(Event()), the return value from Event() is an r-value so can't be passed as an arg requiring a ref. When e is of type Event and passed as a param then this is an l-value and can be passed to a ref param.
May 28, 2022 at 12:22pm
Thanks to both of you so much. I really need a better understanding of (using) const refs. Something I will dive into :)
May 28, 2022 at 12:47pm
Consider this:
1
2
3
void test( int& answer ) {
  answer = 42;
}

That function wants to modify variable of the caller. How would the function call benefit the caller, if the caller could pass unnamed temporary int to the function? The caller could not use the answer that function gives.

Or, what if I call test(7);? One can't modify literal constant either.

While void test(Event&); is about passing data from function to caller, the void test(const Event&); is about passing a value to function without creating a copy (and to some extent, so is void test(Event&&);).
May 28, 2022 at 1:18pm
Thanks. I do understand the concept, I just need a better understanding when (not) to use it.

I'm still running into some problems though. Hopefully you can help me.
I'm trying to create an event-handling-system (which you might already guessed)

I created some classes for the events:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum EventType { KeyPressed, KeyReleased };

class Event
{
public:
  Event(EventType type) : Type(type)
  { }

  EventType Type;
};

class KeyEvent : public Event
{
public:
  KeyEvent(EventType type, int key) : Event(type), Key(key)
  { }

  int Key;
};

class KeyPressedEvent : public KeyEvent
{
public:
  KeyPressedEvent(int key) : KeyEvent(KeyPressed, key)
  { }
};


I create and pass the event:
 
HandleEvent(KeyPressedEvent(key)); // key is received from GLFW 


I have an event-handler like this:
1
2
3
4
5
6
7
8
9
10
11
void HandleEvent(Event e) // or (const Event& e) as considered in my first question
{
  switch (e.Type)
  {
    case KeyPressed:
      // here I would like to cast e to a KeyPressedEvent, but I don't know how
      // I tried KeyPressedEvent ke = (KeyPressedEvent)e;
      // and KeyPressedEvent ke = static_cast<KeyPressedEvent>(e);
      break;
  }
}


It does work when I use pointers and pass new KeyPressedEvent(key) to the event-handler, but doesn't that create too much overhead? Especially when I implement a mousemoved-event which would create a new event on the heap every frame during the move?
Last edited on May 28, 2022 at 1:25pm
May 28, 2022 at 1:31pm
> Is it possible to create a variable and simultaneously pass it as reference to another function?

We need to extend the lifetime of the anonymous temporary object.

One way is to make the parameter an lvalue reference to const, as explained above.

If the object is to be modified by the function, hand over the ownership of the temporary object to the function:
make the parameter an rvalue reference. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>

struct event { int event_id = 999 ; std::string description = {} ; bool handled = false ; } ;

std::vector<event> handled_events ;

void handle( event&& ev ) // handle assumes ownership of the event passed to it
{
    ev.handled = true ;
    handled_events.push_back( std::move(ev) ) ; // and transfers the ownership to the vector
}

int main()
{
    handle( { 22, "test event" } ) ; // rvalue
    handle( {} ) ; // rvalue

    event ev { 33, "another event" } ; // lvalue
    handle( std::move(ev) ) ; // result of std::move is an rvalue
    // don't use ev anymore; it is moved from. (it may be in an unspecified state)
}
Topic archived. No new replies allowed.