I have an interesting predicament here where the code will compile if I pass a variable by value to the function but not if I pass it by reference, and I'm not sure why. In header.h:
#include <iostream>
#include <string>
void get_name(std::string &name)
{
getline(std::cin, name);
return;
}
template <class T, class U>
class readonly
{
friend U;
private:
T data;
T& operator=(const T& arg) {data = arg; return data;}
public:
operator const T&() const {return data;}
};
class myClass
{
private:
typedef readonly<std::string, myClass> RO_string;
public:
RO_string y;
void f()
{
get_name(y); // compile error
}
};
The main.cpp implementation file simply includes this header file, creates an instance of myClass and then calls f(). When doing so, it won't compile properly. The problem lies in the fact that I'm passing the variable y by reference to the get_name function. If I change the function so that I pass by value instead, everything compiles properly and works as expected (except I'm obviously not making changes to y anymore). However, I don't understand this behavior. Why does this happen and is there an optimal way to fix it in this situation?
Your conversion operator:
operator const T&() const {return data;}
Only allows implicit conversion to a const reference. This makes sense since you are interested in making something read only. But get_name requires a non-const reference to a string. That is, get_name expects to be able to mutate its input. The type being passed in cannot convert to a mutable string reference, only to a constant string reference, so it doesn't compile.
When you pass by value, it simply constructs a new string from the const reference to pass into the function, which is fine.
Intuitively, a function called get_name should probably take a const reference since it shouldn't need to mutate its input just to get the name.
As explained by Nir Friedman, get_name(y); invoques the implicit conversion operator which gives you a const reference.
But, as you wrote friend U;, myClass has access to the private member data of y and allows you to do:
get_name(y.data);
Related
I have a class like this:
class MyClass
{
const int GetValue()
{
return 2;
}
}
and I am passing it to a function like this:
void Test(const MyClass &myClass)
{
int x=myClass.GetValue();
}
But I am getting this error:
The object has type qualifiers that are not compatible with member function Object type is const MyClass
I know that if I define function like this:
void Test( MyClass &myClass)
But I want to know why adding const should generate such error?
You need to make the member function const, not the return type:
int GetValue() const;
This makes it possible to call this member on a const instance (or via a const reference or pointer).
Note: as you are returning by value, the constness of the thing you return is decoupled from the constness of the object returning it. In fact, it is very unlikely that you want the return type to be const.
You set the int to const, try making the method const like so:
const int GetValue() const
{
return 2;
}
There are two problems, a visibility problem (your const int GetValue() function is not visible since private) and the fact that you're calling a non-const function (which can perform modifications to the object which has it as a member function)
const int GetValue()
from a constant reference to the class
const MyClass &myClass
you are basically asking for "This object shall not be modified, anyway let me call a function which does not guarantee this".
You're not being coherent.
Given the following code:
#include <iostream>
#include <string>
using namespace std;
class A
{
private:
string m_name;
string m_first;
public:
A(): m_first("string") {}
virtual void print() const {}
string& getName() const {return m_first;} // won't compile
const string& getLastName() const {return m_name;} // compile
};
int main()
{
A a;
return 0;
}
Compiler presents : "invalid initialization of reference of type 'std::string&' from expression of type 'const std::string'"
Why can't I return "m_first" from getName() ? I thought that the const on the tail of the function states that the function will not change 'this'... but I'm not trying to change this , just return a data member.
Because inside a const method, all non-mutable members are implicitly const. So, you're trying to bind a reference to non-const std::string (your return value) to an object of type const std::string, which is illegal(because it would allow modification of const data), hence the error.
By returning a reference, you are saying you can modify the class data-member that the reference-variable is implicitly pointing to, and therefore modify the class ... but you have dedicated the class method as a constant method, meaning it is not allowed to change any class member variables that have not been specifically declared mutable. So by returning a non-constant reference, you are breaking the encapsulation "contract" that the class interface has established. Your options are to either return a temporary object (i.e., that creates a copy of the object), or a constant-reference. So you could either do
const string& getName() const {return m_first;}
or
string getName() const { return m_first; } //copies m_first and returns the copy
Your code promises that the reference won't change the m_name member, but you return a reference that can change it. What you want is a string const& return type.
This returns a "read-only" reference to m_name.
See also: the C++ FAQ on const correctness
When you return string &, it allows modifying class member... but the function is const, so it is not allowed to allow such a situation. But when you return const string &, it doesn't allow modifying class instance.
What if you call A.getName().ModifyTheString() ==> this means you modified this.
I have this code:
template <class T>
class Something
{
T val;
public:
inline Something() : val() {}
inline Something(T v) : val(v) {}
inline T& get() const { return val; }
inline Something& operator =(const Something& a) { val = a.val; return *this; }
};
typedef Something<int> IntSomething;
typedef Something<const int> ConstIntSomething;
class Other
{
public:
IntSomething some_function()
{
return IntSomething(42);
}
ConstIntSomething some_function() const
{
return ConstIntSomething(42);
}
};
void wtf_func()
{
Other o;
ConstIntSomething s;
s = o.some_function();
}
However, the compiler picks the wrong overload of Other::some_function() in wtf_func() (i.e. the non-const one). How can I fix this? Note that for certain reasons I cannot change the name of Other::some_function().
o is not const-qualified, so the non-const some_function is selected. If you want to select the const-qualified overload, you need to add the const qualifier to o:
Other o;
Other const& oref(o);
ConstIntSomething s;
s = oref.some_function();
When overload resolution occurs, the compiler only looks at the o.some_function() subexpression; it does not look at the context around the function call to decide to pick something else. Further, the return type of a member function is not considered during overload resolution.
Note that it may make more sense for IntSomething to be implicitly convertible to ConstIntSomething, either using an operator ConstIntSomething() overload in IntSomething (less good) or using a non-explicit ConstIntSomething(IntSomething const&) constructor in ConstIntSomething (more good).
It doesn't pick the wrong overload; const-ness is resolved by whether this is const or not. In your case, o is non-const, so the non-const overload is picked.
You can hack this by creating a const-reference to o, e.g.:
const Other &o2 = o;
s = o2.some_function();
But really, you should probably be considering your overloads in Something. For instance, you can't currently do this:
IntSomething x;
ConstIntSomething y;
y = x;
which doesn't sound correct. Why shouldn't you be allowed to take a const ref to a non-const ref?
Your object o needs to be const object for a const function to be called on it. Otherwise the compiler rightly picks up the non const version of the function.
The compiler picks the overload to use based on the constness of the object that will become this. You can make it call the desired version with static_cast: s = static_cast<const Other&>(o.some_function());
You might also want to copy the new behaviour found in the containers of the C++0x standard library. Containers such as vector now have members cbegin() and cend() that return a const_iterator whether the container is const or not unlike begin() and end()
class Other {
// Rest of other
public:
// No overload for non-const
// Even if called with a non const Other, since this member is marked
// const, this will be of type Other const * in all cases and will call
// the const qualified overload of some_function.
ConstIntSomething csome_function() const
{
return some_function();
}
};
I define two versions of overloaded operator[] function in a class array. ptr is a pointer to first element of the array object.
int& array::operator[] (int sub) {
return ptr[sub];
}
and
int array::operator[] (int sub) const {
return ptr[sub];
}
Now, if I define a const object integer1 the second function can only be called..... but if I make a non-const object and then invoke as below:
cout << "3rd value is" << integer1[2];
which function is called here?
In your second example, the non-const version will be called, because no conversion is required, and a call that requires no conversion is a better match than one that requires a conversion.
Ultimately, however, you have a basic problem here: what you really want is behavior that changes depending on whether you're using your object as an rvalue or an lvalue, and const doesn't really do that. To make it work correctly, you normally want to return a proxy object, and overload operator= and operator T for the proxy object:
template <class T>
class myarray {
T *ptr;
class proxy {
T &val;
proxy &operator=(proxy const &p); // assignment not allowed.
public:
proxy(T &t) : val(t) {}
operator T() const { return val; }
proxy &operator=(T const&t) { val = t; return *this; }
};
proxy const operator[](int sub) const { return proxy(ptr[sub]); }
proxy operator[](int sub) { return proxy(ptr[sub]); }
// obviously other stuff like ctors needed.
};
Now we get sane behavior -- when/if our array<int> (or whatever type) is const, our operator[] const will be used, and it'll give a const proxy. Since its assignment operators are not const, attempting to use them will fail (won't compile).
OTOH, if the original array<int> was not const, we'll get a non-const proxy, in which case we can use both operator T and operator=, and be able to both read and write the value in the array<int>.
Your const version should return const int& not int, so that the semantics are just the same between the two functions.
Once you've done that, it doesn't matter which one is used. If the const version has to be used because your object has a const context, then it will be... and it won't matter as you're not trying to modify anything. Otherwise, it'll use the non-const version... but with just the same effect.
I'm building a reference counting system and have defined a prototype template class which represents all references to objects that will be used.
The point where I am stuck is that I have to differentiate between const and non const access to those objects through a reference. If the access is const (read-only, or called method of underlying object is flagged const), everything is okay - however if it's not - a copy of the object might have to be created first.
A simplified version of my reference class:
template< class T >
class CRef
{
protected:
T* ptr;
public:
T* const* operator ->() const { return ptr; };
T* operator ->() { printf( "Non-const access!" ); return ptr; };
};
The problem is, that only the non-const -> operator overload function gets called, even when accessing const functions of the underlying object type.
How do I get the constant dereferencing overloading function to get called properly?
The const one is called on const objects. If you want the const -> called, cast the object reference you have to a const or provide a way to get a const version of the object.
const / non-const cannot, in principle, be used to differentiate between read and write access.
You’re out of luck here. The only way to accomplish this is via a proxy object that is returned by operator -> and that handles reading via an implicit conversion:
The returned proxy defines an operator T (implicit conversion) that is invoked when it is assigned to another object, and an operator =(T const&) when something is assigned to it.
template <typename T>
class CRef {
…
CRefElementProxy<T> operator ->() const { return CRefElementProxy<T>(this); };
friend CRefElementProxy;
};
template <typename T>
class CRefElementProxy {
operator T const*() const { return parent->ptr; }
CRefElementProxy operator =(T const* value) {
printf("Non-const access!");
return parent->ptr;
}
};
That’s just a pseudo-codey draft. The real version looks a bit more complex.
Once the pointer has been returned then there is no way to know what is done with it.