Home > Back-end >  In C why is it when I reassign a reference variable to a different reference variable it creates a
In C why is it when I reassign a reference variable to a different reference variable it creates a

Time:04-17

struct configfs
{
    int & foo;
    configfs(int & foo_) : foo(foo_) {}

    void set_foo(int & foo_)
    {
        foo = foo_;
    }

};

int main() { 
    int a = 1;
    configfs conf(a);
    a = 3;
    std::cout << conf.foo; // 3
    std::cout << a;      // 3

    int b = 2;
    conf.set_foo(b);
    b = 9;
    std::cout << conf.foo; // 2
    std::cout << b;        // 9

    return 0;
}

When I initialize configfs with int a and then alter a in the main function it also alters configfs.foo . But when I reassign configfs.foo to a new reference and alter that reference in the main function, the behavior is different. Why? Is there a way so that when I run conf.set_foo(b) and then alter b in the main scope, it also alters conf.foo as well?

CodePudding user response:

Refercences can only be "assigned" during initialization. They cannot be "retargeted" the way like pointers. After the initialization the memory the reference refers to remains fixed and any assignment using = results in the invokation of the assignment operator to the memory the reference refers to. That's the way it's specified in the C standard.

You could rewrite the example using a custom type with assignment operators printing the information about calls:

template<class T>
struct configfs
{
    T& foo;
    configfs(T& foo_) : foo(foo_) {}

    void set_foo(T& foo_)
    {
        foo = foo_; // this uses the assignment operator assigning to the variable foo is an alias for
    }

};

// Type wrapping the int and printing out the operations applied
struct TestWrapper
{
    TestWrapper(int value)
        : value(value)
    {
        std::cout << "TestWrapper::TestWrapper(" << value << ")\n";
    }

    TestWrapper(TestWrapper const& other)
        : value(other.value)
    {
        std::cout << "TestWrapper::TestWrapper(TestWrapper{" << other.value << "})\n";
    }

    TestWrapper& operator=(TestWrapper const& other)
    {
        std::cout << "TestWrapper::operator=(TestWrapper{" << other.value << "})\n";
        value = other.value;
        return *this;
    }

    int value;
};

std::ostream& operator<<(std::ostream& s, TestWrapper const& val)
{
    s << val.value;
    return s;
}

int main() {
    TestWrapper a = 1;
    configfs<TestWrapper> conf(a);
    a = 3;
    std::cout << conf.foo << '\n'; // 3
    std::cout << a << '\n';      // 3

    TestWrapper b = 2;
    conf.set_foo(b); // TestWrapper::operator=(TestWrapper{2})
    b = 9;
    std::cout << conf.foo << '\n'; // 2
    std::cout << b << '\n';        // 9

    return 0;
}

CodePudding user response:

When you wrote:

conf.set_foo(b);

The following things happen:

  1. Member function set_foo is called on the object conf.

  2. Moreover, the reference parameter named foo_ is bound the the argument named b. That is, b is passed by reference.

  3. Next, the statement foo = foo_; is encountered. This is an assigment statement and not an initialization. What this does is that it assigns the value referred to by the parameter foo_ to the object referred to by the data member foo(which is nothing but a here). You can confirm this by adding std::cout<<a; after the call to set_foo as shown below:

int b = 2;
conf.set_foo(b);
std::cout<<a<<std::endl;  //prints 2

This is because operations on a reference are actually operations on the object to which the reference is bound. This means that when we assign to a reference, we are assigning to the object to which the reference is bound. When we fetch the value of a reference, we are really fetching the value of the object to which the reference is bound.


Note:
Once initialized, a reference remains bound to its initial object. There is no way to rebind a reference to refer to a different object.

  • Related