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.

jeffreyveon
  • 13,400
  • 18
  • 79
  • 129
  • 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. – juanchopanza Nov 27 '14 at 13:40
  • 4
    "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? – molbdnilo Nov 27 '14 at 13:48
  • Oops, I left a *not*! – jeffreyveon Nov 27 '14 at 15:22

3 Answers3

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.

Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107
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;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 4
    This is not what urefs are for. If you want a read only view of a variable you should simply take it by `const&`. – Stephan Dollberg Nov 28 '14 at 05:52
  • @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. – vsoftco Nov 28 '14 at 05:54
  • 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. – Stephan Dollberg Nov 28 '14 at 05:56
  • @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? – vsoftco Nov 28 '14 at 05:59
  • @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. – vsoftco Nov 28 '14 at 06:08
  • [*Forwarding references*](https://isocpp.org/files/papers/n4262.pdf) should really be used only for forwarding. – T.C. Nov 28 '14 at 06:09
  • @T.C. didn't make too much out of your link, are they not the same? – vsoftco Nov 28 '14 at 06:11
  • @vsoftco The point is that `T&&` shouldn't be used the way you are using it. – T.C. Nov 28 '14 at 06:15
  • @T.C. so I guess one shouldn't use urefs unless for forwarding, right? – vsoftco Nov 28 '14 at 06:28
  • @bamboon thanks, will take a look before going to bed :) – vsoftco Nov 28 '14 at 06:33
  • If you enable all the warnings or at least /W4, you get warning that you are using a non-standard extension. – Jagannath Dec 01 '14 at 01:09
  • @Jagannath 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? – vsoftco Dec 01 '14 at 01:39
  • @vsoftco sorry my comment was supposed to be for the below answer. I mistakenly put it to your answer. – Jagannath Dec 01 '14 at 02:52
-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;
}
telcom
  • 145
  • 10
  • 2
    No, this shouldn't compile. This must be some MSVC extension again. – Stephan Dollberg Nov 27 '14 at 14:27
  • 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. – telcom Nov 27 '14 at 14:37
  • 1
    You can avoid it but for a VS user it's okay USE it. – telcom Nov 27 '14 at 14:49
  • I'm a VS user and sometimes need to give my code to people who have unknown C++ compilers. – Neil Kirk Nov 27 '14 at 15:24
  • If you want to write portable code (and not compile just for Windows), even if you use VS you should use standard C++ – vsoftco Nov 28 '14 at 00:44
  • MSVC has a dangerous extension that allows non-const lvalue references to bind to rvalues. You shouldn't use it. – T.C. Nov 28 '14 at 06:16