Invalid initialization of non-const reference of type
Asked Answered
R

3

18

In the following code, I'm not able to pass a temporary object as argument to the printAge function:

struct Person {
  int age;
  Person(int _age): age(_age) {}
};

void printAge(Person &person) {
   cout << "Age: " << person.age << endl;
}

int main () {
  Person p(50);
  printAge(Person(50));  // fails!
  printAge(p);
  return 0;
}

The error I get is:

error: invalid initialization of non-const reference of type ‘Person&’ from an rvalue of type ‘Person’

I realize that this is something to do with passing an lValue to a function expecting a rValue... Is there a way to convert my lValue to rValue by using std::move or something? I tried taking a constant parameter, but that does not seem to work.

Runic answered 27/11, 2014 at 13:38 Comment(3)
It is the other way around. You are passing an rvalue to a function expecting an lvalue. Non-const lvalue references cannot bind to rvalues, but you can use a const lvalue reference instead.Monmouthshire
"I tried taking a constant parameter, but that does seem to work." So you solved the problem, but decided to discard the solution because it seems to work?Violetvioleta
Oops, I left a not!Runic
B
18

Simply make your print function take your argument by const&. This is also logically right as it doesn't modify your argument.

void printAge(const Person &person) {
   cout << "Age: " << person.age << endl;
}

The actual problem is the other way around. You are passing a temporary(rvalue) to a function which expects an lvalue.

Barnwell answered 27/11, 2014 at 13:39 Comment(0)
V
10

Or, if you have a C++11-compliant compiler, can use the so called universal reference approach, which, via reference collapsing rules, can bind to both lvalue and rvalue references:

#include <iostream>
using namespace std;

struct Person {
  int age;
  Person(int _age): age(_age) {}
};

template<typename T> // can bind to both lvalue AND rvalue references
void printAge(T&& person) {
   cout << "Age: " << person.age << endl;
}

int main () {
  Person p(50);
  printAge(Person(50));  // works now
  printAge(p);
  return 0;
}

Or, in C++14,

void printAge(auto&& person) {
   cout << "Age: " << person.age << endl;
}
Vento answered 27/11, 2014 at 16:20 Comment(13)
This is not what urefs are for. If you want a read only view of a variable you should simply take it by const&.Barnwell
@bamboon can you expand on this? didn't really understand your comment. are you saying that urefs are just for perfect forwarding? urefs work perfectly here though. Ok, saw your updated comment, agree.Vento
Yeah, basically. While I don't think that it's really wrong I think that it's terribly over-complicating the problem and the reason for why nobody knows anymore how to properly pass variables(e.g.: see Herb Sutter's talk at cppcon). Edit: Ah yeah, I somehow hit enter too fast there the first time.Barnwell
@bamboon correct :) I wouldn't be so depressed as saying that no one knows how to pass by const reference though :) And I wrote the solution without really thinking at the name printAge, as one could as well had something called modifyAge. But again, I agree, one should first know what const type& x is before learning what auto&& x really means and what reference collapsing rules are. can you post a link to Herb's talk?Vento
@bamboon also that's why I think now "universal references" are replaces by "forwarding references", although I am not 100% sure about this. Anyway, good comment.Vento
Forwarding references should really be used only for forwarding.Porty
@Porty didn't make too much out of your link, are they not the same?Vento
@Vento The point is that T&& shouldn't be used the way you are using it.Porty
@Porty so I guess one shouldn't use urefs unless for forwarding, right?Vento
@bamboon thanks, will take a look before going to bed :)Vento
If you enable all the warnings or at least /W4, you get warning that you are using a non-standard extension.Overwhelming
@Overwhelming I don't think there is any non-standard extension here. The construction is perfectly valid C++11/C++14. Can you be more specific and say which line represents a non-standard extension?Vento
@Vento sorry my comment was supposed to be for the below answer. I mistakenly put it to your answer.Overwhelming
A
-2

Your code doesn't work if you run g++ or gcc compilers. You need to add const to void printAge(const Person &person). However, in Visual Studio it will work fine. I've tested for VS2010 and VS2012 and in both the following code works fine.

 #include<iostream>

using namespace std;
struct Person {
  int age;
  Person(int _age): age(_age) {}
};

void printAge(Person &person) {
   cout << "Age: " << person.age << endl;
}

int main () {
  Person p(50);
  printAge(Person(50));  // DOES NOT fail!
  printAge(p);
  return 0;
}
Alessi answered 27/11, 2014 at 14:20 Comment(6)
No, this shouldn't compile. This must be some MSVC extension again.Barnwell
Yes, I've just checked in g++ or gcc; they both fail to compile it; but in VS it's very normal to pass an object as an argument although it's not defined const; it should be avoided though.Alessi
You can avoid it but for a VS user it's okay USE it.Alessi
I'm a VS user and sometimes need to give my code to people who have unknown C++ compilers.Erlina
If you want to write portable code (and not compile just for Windows), even if you use VS you should use standard C++Vento
MSVC has a dangerous extension that allows non-const lvalue references to bind to rvalues. You shouldn't use it.Porty

© 2022 - 2024 — McMap. All rights reserved.