21

I need to create a Bar object, which has a private object Foo f.

However, the value of Foo object parameter should be passed by the specific method int genValue().

If I initialize f in the constructor scope Bar(){...}, the compiler yell error, something like there is no constructor Foo().

If I construct like this Bar(): f(genValue()), the compiler yells the error:

test.cpp: In constructor ‘Bar::Bar()’:
test.cpp:16:19: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
 Bar(): f(genValue()){    
            ~~~~~~~~^~
test.cpp:7:2: note:   initializing argument 1 of ‘Foo::Foo(int&)’    
 Foo(int &x) {    
 ^~~

Example code:

class Foo {
public:
    Foo(int &x) {
        this->x = x;
    }
private:
    int x;
};

class Bar {
public:
    Bar(): f(genValue()){
    }
private:
    Foo f;

    int genValue(){
        int x;
        // do something ...
        x = 1;
        return x;
    }
};

int main() {

    Bar bar ();

    return 0;
}

How can I fix the problem, if I don't want to modify Foo class and its argument value should be passed from genValue()? And, I don't want to use pure pointer (*), but a solution with smart pointer is okay!

peterh
  • 11,875
  • 18
  • 85
  • 108
hsuchengmao
  • 524
  • 1
  • 6
  • 14
  • as-is, there's no point in having `Foo` take a reference if at the end you're copying. Just change Foo's constructor to `Foo(int x) : x(x) { }` – Jans Jan 04 '19 at 04:06

2 Answers2

20

A non-const reference parameter, such as an int&, can only refer to an "lvalue," which is a named variable.

auto takes_nonconst_reference = [](int&){};
auto takes_const_reference = [](const int&){};
auto takes_value = [](int){};
auto returns_int = []{return 42;};

int foo = 1;

// OK
takes_nonconst_reference(foo);
takes_const_reference(foo);
takes_const_reference(returns_int());
takes_value(foo);
takes_value(returns_int());

// compilation error, value returned from a function is not a named variable
takes_nonconst_reference(returns_int());

In this particular case, since your class is storing a copy of the constructor parameter, you should pass it by value (int, not int& nor const int&).

Ray Hamel
  • 1,289
  • 6
  • 16
  • 1
    named variables may be lvalues, but not all lvalues are named variables. These are also lvalues: `v.at(5)`, `*this`, `*(p + 42)` – Ben Voigt Jan 04 '19 at 04:31
  • 1
    @BenVoigt I'm deliberately oversimplifying since OP appears to be a beginner. – Ray Hamel Jan 04 '19 at 04:59
  • oversimplification is what got OP into this trouble – Ben Voigt Jan 04 '19 at 05:02
  • 3
    Getting into all the details of the various value categories isn't going to be at all helpful to a beginner and will just serve to confuse and discourage. "lvalues are named variables and rvalues are temporaries" is a good enough heuristic for a beginner, and no more an "oversimplification" than "I before E except after C" is for English. – Ray Hamel Jan 04 '19 at 05:10
  • You don't have to go into detail of all 5 value categories to have an accurate definition of an lvalue, specifically that it has a storage location, which can be found either by name lookup, or by a handle (pointer or reference). – Ben Voigt Jan 04 '19 at 05:17
  • Storage location is an intermediate-level concept. Given his code sample, I doubt that OP even knows what a pointer is yet. – Ray Hamel Jan 04 '19 at 05:22
  • 2
    Plus if we're being pedantic certain lvalues have no storage location, such as non-type template parameters and `[[no_unique_address]]` empty data members. – Ray Hamel Jan 04 '19 at 05:27
  • In case anyone is wondering, the `takes_nonconst_reference` case is invalid because a non-const reference allows changing the value through the reference, but it is illegal to change the value of rvalues, the same way `42 = anything` is illegal. – Rufus May 14 '20 at 06:10
13

Don't pass int&, it can't be bound to a constant or temporary because those can't be modified - use const int& instead.

Actually for simple types you should prefer to pass by value instead, and let the optimizer worry about providing the best implementation.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622