2

I have a variable ntot_sols who's final value isn't known when my object is constructed.

Consequentially I want to store a reference to the variable instead of the value since it's subject to change.

I think the following code will do it.

struct ItemWeighter { int nsols; int ntot_sols; ItemWeighter(int _nsols, int& _ntot_sols) { nsols = nsols; ntot_sols = _ntot_sols; } float weight() { return nsols / float(ntot_sols); } }; 

But I'm surprised that ntot_sols is type int and not int &. How does the variable know that it's a reference?

5
  • ntot_sols is not a reference. Commented Nov 5, 2021 at 22:03
  • If I put in int & ntot_sols then the linter gives me an error saying that ntot_sols wasn't initialized Commented Nov 5, 2021 at 22:04
  • You would need to put it in the initializer list. Commented Nov 5, 2021 at 22:11
  • If you have a data member that is a reference, you need to initialize it through the constructor. The constructor parameter must be a reference and you should be using an initializer list. Commented Nov 5, 2021 at 22:12
  • What you are trying to do sounds like a bad idea. What’s the lifespan of the referred variable? What’s the lifespan of your structure? Commented Nov 5, 2021 at 22:18

1 Answer 1

2

You can't use the assignment operator to bind a reference. It has to be bound when first created. For a class member variable, this means you have to do it in a member initializer list; the body of the constructor is too late.

Example:

#include <iostream> class foo { private: int& x; public: foo(int& other_var) : x(other_var) { } ~foo() { } void print_x() { std::cout << x << std::endl; } }; int main() { int a = 5; foo my_foo(a); my_foo.print_x(); a = 10; my_foo.print_x(); return 0; } 

Try on godbolt. It prints out:

5 10 

Note that such code, where your object holds a reference to another variable created elsewhere, can be risky. It is up to you to make sure that the referenced int has a lifetime at least as long as the foo object. It can be easy to get this wrong, and the compiler won't help you. Consider for instance:

foo make_a_foo() { int a = 7; foo f(a); return f; } void other_func() { foo g = make_a_foo(); g.print_x(); // undefined behavior! } 

Here g.x is a reference to the local variable a from make_a_foo(), whose lifetime ended when make_a_foo() returned.

It may be better after all to make x an ordinary int member variable, instead of a reference, and call a setter function to update its value when needed.

Sign up to request clarification or add additional context in comments.

7 Comments

How come I have to use an initializer list instead of defining it in the constructor function? Is a reference similar to const somehow?
@financial_physician: That's just how the language works. The idea is that it should never be possible to have a reference that isn't bound to some object. If you could start executing the body of the constructor before initializing the reference, then you could potentially access it before initializing, e.g. foo(int& a) { std::cout << x; x = a; }. That should be impossible and prevented at compile time. [...]
@financial_physician: Instead of making the compiler analyze the statements in the body, it is easier to put the initializations before the body even starts, so that it is inherently impossible for the body to access an uninitialized reference. It is a similiar situation with const members (there must never be a time when they are uninitialized) and with members that are classes (there must never be a time when they have not been constructed).
Makes sense, but then I'd argue that any variable should be initialized in the initializer list and not just the references
@financial_physician: Yes. that is good practice. For an ordinary variable like int, it's optional, because you are allowed to have such variables uninitialized until you assign to them later.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.