Reference-returning method

R

Ruben Campos

Is the next code legal in standard ANSI C++? (I've suppressed constructors,
destructors and extra stuff both for focusing in the real problem and for
readability).

class ClassA
{
public:
void f ( ) { /* ... */ }
};

class ClassB
{
public:
ClassA & GetARef ( ) { return mData; }
private:
ClassA mData;
};

int main (int argn, char ** argc)
{
ClassB b;
b.A().f();
}

And if ClassB::GetARef was declared as const, and ClassB::mData as mutable?

I've encountered this problem after disabling Microsoft extensions to the
languageC++ in MS Visual Studio .NET 2003, while I could compile wit those
extensions activated without any problem. I don't know if it's a compiler
matter, or if I am not fitting to the standard.

Thank you very much in advance.
 
A

Alf P. Steinbach

* Ruben Campos:
Is the next code legal in standard ANSI C++? (I've suppressed constructors,
destructors and extra stuff both for focusing in the real problem and for
readability).

class ClassA
{
public:
void f ( ) { /* ... */ }
};

class ClassB
{
public:
ClassA & GetARef ( ) { return mData; }
private:
ClassA mData;
};

int main (int argn, char ** argc)
{
ClassB b;
b.A().f();
}

The name "A" isn't defined anywhere, so this should not compile.

If instead you had written


b.GetARef().f();


it would be OK.

And if ClassB::GetARef was declared as const, and ClassB::mData as mutable?

Still OK language-wise, but probably a design error.
 
M

Matthias =?ISO-8859-1?Q?K=E4ppler?=

Alf said:
Still OK language-wise, but probably a design error.

Even if non-const it's a flawed design. Returning non-const references to
class internals takes any sense out of implementation/data hiding and
encapsulation.

Regards,
Matthias
 
A

Alf P. Steinbach

* Matthias =?ISO-8859-1?Q?K=E4ppler?=:
Even if non-const it's a flawed design. Returning non-const references to
class internals takes any sense out of implementation/data hiding and
encapsulation.

As a general rule I agree.

But then you have such things as std::string::c_str (that's a pointer
but the principle is the same).

The reason I wrote "probably" is that I've encountered cases where
exactly what the OP suggested was reasonable. For example, in a C++
interface to JNI (the Java <-> native binding) a wrapper for a JNI
UTF-8 string might provide a member function

char const* c_str() const

that computes and caches the 'char' based string on demand. Caching
is one of the most common reasons for using 'mutable' (to the degree
that 'mutable' is used in good designs).
 
M

Matthias =?ISO-8859-1?Q?K=E4ppler?=

Alf said:
The reason I wrote "probably" is that I've encountered cases where
exactly what the OP suggested was reasonable. For example, in a C++
interface to JNI (the Java <-> native binding) a wrapper for a JNI
UTF-8 string might provide a member function

char const* c_str() const

that computes and caches the 'char' based string on demand. Caching
is one of the most common reasons for using 'mutable' (to the degree
that 'mutable' is used in good designs).

Yes, or reference counting.
'mutable' IMO is also reasonable if the variable which is changed does not
change the actual state of the object. So, in my eyes it's always
reasonable to use mutable whenever the -expected- state of the object
doesn't change. The client normally wouldn't consider an object's state
changed if some ref counter is incremented in a const method.

Regards,
Matthias
 
R

Ruben Campos

First of all, thank you very much for your answers. In fact, I should have
written "GetARef" instead of "A".

Now, I've noticed that error I found comes from the fact that I'm using
templates. Please, take a look to this code:

template <typename T>
class CBase
{
// ...

protected:
T & AccessData () { return mData; }
const T & AccessData () const { return mData; }

private:
T mData;
};

template <typename T>
class CDerived : public CBase <T>
{
public:
T GetData () const { return AccessData(); }

protected:
using CBase <T>::AccessData;
};


int
main (int argn, char ** argv)
{
CDerived d;
d.GetData();
}

This code compiles and runs without problems, but I've noticed that it only
works fine if I include the using declaration in CDerived; otherwise,
compiler returns a non-defined symbol (AccessData) error. I've surprisingly
seen that this using declaration is only necessary when both base and
derived classes are templates; with a non-template base class, OR with a
non-template derived class, OR with non-template both base and derived
classes, this using declaration is not necessary. Could you explain me the
reason of this?

Not related, I've noticed that writting a using declaration for a base class
method, within an access modifier (public, protected or private), is enough
to change the access modifier originally defined in the base class for that
method. Is this right?

Once again, thank you very much in advance.
 
J

Jonathan Mcdougall

Ruben said:
First of all, thank you very much for your answers. In fact, I should have
written "GetARef" instead of "A".

Now, I've noticed that error I found comes from the fact that I'm using
templates. Please, take a look to this code:

template <typename T>
class CBase
{
// ...

protected:
T & AccessData () { return mData; }
const T & AccessData () const { return mData; }

private:
T mData;
};

template <typename T>
class CDerived : public CBase <T>
{
public:
T GetData () const { return AccessData(); }

protected:
using CBase <T>::AccessData;
};


int
main (int argn, char ** argv)
{
CDerived d;
d.GetData();
}

This code compiles and runs without problems, but I've noticed that it only
works fine if I include the using declaration in CDerived; otherwise,
compiler returns a non-defined symbol (AccessData) error. I've surprisingly
seen that this using declaration is only necessary when both base and
derived classes are templates; with a non-template base class, OR with a
non-template derived class, OR with non-template both base and derived
classes, this using declaration is not necessary. Could you explain me the
reason of this?

AccessData is a dependent name, that means its actual definition depends
on the template parameter of CBase. Dependent names are not resolved by
the compiler at this stage so the functions are not found. By injecting
the names with a using _directive_ (not declaration), you explicitly
tell the compiler which function to use.
Not related, I've noticed that writting a using declaration for a base class
method, within an access modifier (public, protected or private), is enough
to change the access modifier originally defined in the base class for that
method. Is this right?

Yes it is, as long as the derived class has access to that name.


Jonatan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top