Monday, June 29, 2009

Accessor functions

In C++, a temporary cannot be passed by non-const reference. If you are slightly inexperienced and (for the sake of encapsulation) writing your accessors like this:

class Object
{
....
MPI::Intracomm GetRowWorld() { return rowWorld; }
MPI::Intracomm GetColWorld() { return colWorld; }
...
}


You'll be in deep trouble when you want to send these return values to a function. Such as this:

void foo(MPI::Intracomm & rowcomm, MPI::Intracomm & colcomm)
{
...
}

Object A;
foo( A.GetRowWorld(), A.GetColWorld());
// Won't compile (and you should be happy about it)



So, what's the fix? You can try to change the signature of foo:
void foo(const MPI::Intracomm & rowcomm, const MPI::Intracomm & colcomm);

You might think this is smarter as it compiles but now you are in even deeper trouble because your accessor functions return values and they go out of scope immediately. Therefore, when foo starts executing, the references are dangling. You might try to change the signature to this:
void foo(MPI::Intracomm rowcomm, MPI::Intracomm colcomm);

This works, but hey it might be terribly inefficient if MPI::Intracomm is a big object because you're creating temporaries everytime you pass by value. Just leave the signature as is and go fix your class definition instead:

class Object
{
....
MPI::Intracomm & GetRowWorld() { return rowWorld; }
MPI::Intracomm & GetColWorld() { return colWorld; }
MPI::Intracomm GetRowWorld() const { return rowWorld; }
MPI::Intracomm GetColWorld() const { return colWorld; }
...
}

4 comments:

  1. I don't think const references leave the pointers dangling as the they extend the life of the temporaries, as Herb Sutter says.

    Wow, I can't copy paste the corresponding link on this textbox :( just google "A candidate for the most important const" in google and you will see what I mean. ayrica, nerede sicisin :)

    ReplyDelete
  2. Thanks Ozgur, awesome pointer. On the other hand, I still think that implementing the get/set functions to return a reference for set() and a const object for get() is less error prone. There are some exceptions to const references extending the lifetime of temporaries (such as in constructors) and I feel slightly uneasy about them. More importantly, what if I wanted to change the value of the object inside foo()?

    ReplyDelete
  3. you are right about restriction of const parameters in the event of assigning to them.

    by the way, what is the reason of returning by value in const getters, rather than by reference - which prevents copying?

    ReplyDelete
  4. First of all, the getter must be const otherwise such a code wouldn't compile:

    foo(const & Object obj)
    {
    printWorld(obj.GetRowWorld());
    }

    Then the question about returning a reference or an actual object remains. If it was a reference, anybody would be able to change the value returned by a function that was intended to be an accessor only (yes, I can make it return a const reference but constness can easily be casted away)

    Writing get/set functions this way is a habit I picked while implementing vector-like classes:

    T & operator[](int index);
    const int operator[](int index) const;

    But again, you have a point and maybe I should consider returning const references for big objects.

    ReplyDelete