const correctness with smart pointers - c++

I'm trying to understand better how does const-correctness work and more specifically, when dealing with classes whose members are based on containers and smart pointers.
I guess that the const-correctness property is the same regardless of the class members. However, since I'm having some difficulties to clearly understand what's going on,
I decided to ask you for advice.
So, here is the context. I've a ShapeContainer class that has as private class member a vector of smart pointers.
The Shape class is abstract and has the following virtual function virtual float doSomething(); which is then redefined by its derived classes. Note that it's a non-const class function.
The relevant part of the code is given below:
class ShapeContainer{
public:
typedef std::shared_ptr<Shape> ShapePtr;
typedef std::vector<ShapePtr> ShapePtrContainer;
// .......
const ShapePtr & operator[]( int ) const { return m_vect[index]; }; // const version
// ShapePtr & operator[]( int ) { return m_vect[index]; }; // non-const version
// .......
private:
ShapePtrContainer m_vect;
};
class Shape{
public:
// ...
virtual float doSomething() = 0;
};
Here are my questions.
Q1. Why do I'm allowed to call the doSomething() function in the following way: int index = 0; float tmp = container1[index]->doSomething(); (having ShapeContainer container1=createBasicShapes();)?
From what I understand, after calling to the const ShapePtr operator[] const function we'll get a const pointer to a Shape object, however the doSomething() virtual
function is not const. So, how does a reference to a const-object can call a non-const function?
Q2. By calling the doSomething() function as previouly ilustrated (float tmp =container1[index]->doSomething();) and by adding a non-const version of the operator[], this latter
overloaded version is then called instead of the const-version one. Why does it is so?
Now, instead of having a ShapeContainer class, I've now a new class named ShapeContainerInfo that still has a vector but of an intermediate ShapeInfo class (that has a smart pointer as a class member).
class ShapeContainerInfo{
public:
typedef std::vector<ShapeInfo> ShapeContainer;
const ShapeInfo & operator []( int index) const { return m_vect[index]; };
// ShapeInfo & operator []( int index) { return m_vect[index]; }; // non-const version
private:
ShapeContainer m_vect;
};
class ShapeInfo{
public:
typedef std::shared_ptr<Shape> ShapePtr;
// ...
float doSomething(){ return m_ShapePtr->doSomething(); };
private:
ShapePtr m_ShapePtr;
int m_nID;
};
Q3. When I call float tmp = container2[i].doSomething();, I get the following compiler error: error C2662: 'ShapeInfo::doSomething' : cannot convert 'this' pointer from 'const ShapeInfo' to 'ShapeInfo &'.
However, when I add a non-const vesion of the overloaded operator [] the compiler error is gone. So, why do I really need the non-const operator[] for ShapeContainerInfo and not for ShapeContainer?
Q4. If the m_vect private member of ShapeContainerInfo is set now as public member and only the const-version of operator[] is defined (not the non-const one), there are no compiler error messages. Why this? e.g. after setting m_vect to be a public class member: float tmp = info.m_vect[i].doSomething();
Q5. How could I correctly define both the ShapeInfo and ShapeContainerInfo classes such that I only need to define the const-version of the operator[] and still being able to call the float doSomething() function?
For those of you interested in the whole sample code, please find it here.
Clarifications, suggestions are always welcomed :-)
Merci!

Q1: The shared_ptr is const, that doesn't mean that the pointed to object is const. For that you would want shared_ptr<const Shape>.
Q2: Since you're ShapeContainer was not const, the non-const function was a better match, so it was called instead of the const version.
Q3: vector propagates its constness to its elements. shared_ptr does not. This is inline with the behavior of arrays and raw pointers. The elements of const arrays are const. The thing pointed to by a const pointer is not (necessarily) const.
Q4: Are you saying this produces no error?
ShapeContainerInfo info;
info[0].doSomething();
Please clarify, because that should be an error.
Q4: Okay, so you're saying that this produces no error:
ShapeContainerInfo info;
info.m_vect[0].doSomething();
Nor should it. The vector is not const. It's only inside the const member function that the vector(and all other members) are treated as const.
Q5: Make m_vect a vector of unique pointers. Inside the const function, the vector itself will be const, and the unique pointers will be const. But the objects that the unique pointers point to will be mutable.
As an example, the set function in this class is not legal:
struct Foo
{
void set(int index, int value) const
{
v[index] = value;
}
std::vector<int> v;
};
But this one is:
struct Foo
{
void set(int index, int value) const
{
*v[index] = value;
}
std::vector<std::unique_ptr<int>> v;
};

Related

Is it possible to declare a class member both const/non-const?

I have a class that can take both a non-const pointer or a const pointer as arguments to its overloaded constructors. In my particular case, I need to instantiate an object of this class from both const and non-const methods of class T, but it fails from const methods, as it can't assign the const pointer to foo .
myClass() {
public:
myClass(T* v);
myClass(const T* v);
// ...
T* foo;
// ...
}
Is it possible to assign the argument in both constructors to foo? If so, what would be the correct syntax?
EDIT:
In a more specific case, I have a class myClass that wraps around std::vector and allows to me to directly access subsets of a vector through a nested class mySubset:
template<typename _type>
myClass() {
std::vector<_type> data;
public:
class mySubset(){
myClass<type>* foo;
public:
mySubset(myClass<_type>* _in) { foo = _in; };
mySubset(const myClass<_type>* _in) { foo = _in; /* error */ };
// ...
}
// ...
myClass();
// ...
void mySubset method() { return mySubset(this); };;
void mySubset const_method const() { return mySubset(this); /* error */ };
// ...
}
The code within is irrelevant -basically mySubset allows to both read and write to specific vector positions. While I'm able to achieve what I want with separate const and non-const nested classes, I was looking for a way to do this with a single return type.
I think you'll have to reconsider your design since you can't initialize a T* with a const T* lvalue, without const_cast which should be avoided unless you're really really sure, (since it invokes an undefined behavior if you try to modify a const pointer after casting away its constness)
Instead, you could use template type deduction for const and non const
template <typename T>
class myClass {
public:
//myClass(T* v):foo(v) { }
myClass( T* v):foo(v)
{
}
// ...
T* foo;
// ...
};
Then,
int a =42;
const int* p1 = &a;
int *p2 = &a;
myClass X1(p1); //C++17 auto type deduction or use myClass<const int> X1(p1)
myClass X2(p2);
You could using const_cast in your const T* constructor, but typically you shouldn't.
const T* means "point to a constant value T", and you store a "pointer to a T". If you do a const cast, you could end up modifying a value which shouldn't be modified. If you aren't going to modify foo, just declare it const T* and just use the single const T* constructor.
I'd check to see if this is a design issue. A lot of the times these scenarios appear:
(1) Where you're storing a pointer to something as non-const when it should be const. Typically this is because you're accessing values in another object and you should be passing the object as a (possibly const) reference at each use site rather than storing a pointer to it.
(2) When you really want to store a copy of an object, in which case you just keep a regular T and pass it in as const T& in the constructor.
(3) You're dealing with raw C-style strings and want to copy the contents into your own buffer.
If you don't want to use parameterized-type (template) class as #P0W's answer, it is not possible you can use only one pointer to accept all constant and non-constant pointer type. You need another constant pointer type to accept only const <your another class> * in your wrapper class.
Below code works after you have two separate pointer types in wrapper class which you may not like.
#include <iostream>
using namespace std;
class SomeObject {
public:
SomeObject(){}
explicit SomeObject(int i):testVal(i){}
private:
int testVal;
};
class PtWrapper {
public:
PtWrapper(SomeObject *pso);
PtWrapper(const SomeObject *cpso);
private:
SomeObject *pSO;
const SomeObject *cpSO;
};
int main(int argc, char *argv[]) {
SomeObject so(133);
SomeObject *pso = &so;
const SomeObject cso(166);
const SomeObject *cpso = &cso;
PtWrapper pw1(pso);
PtWrapper pw2(cpso);
return 0;
}
PtWrapper::PtWrapper(SomeObject *pso) :pSO(pso){
}
PtWrapper::PtWrapper(const SomeObject *cpso):cpSO(cpso){}

C++ Preventing const methods from changing data through a member pointer or reference

Say I have a simple class like this
class Foo
{
public:
void foo()const
{
str[5] = 'x';
obj->changeTheWorld();
x = 4;
y.get() = 5;
obj2->changeTheWorld();
}
private:
char *str; //some referenced data, not owned by Foo
ComplexObj *obj; //some referenced data, not owned by Foo
int &x; //references as well
//wrapped reference, but has a "T& get()const"
std::reference_wrapper<int> y;
//an occasionally useful pointer wrapper for complex memory cases
//but has a "T* get()const"
std::shared_ptr<ComplexObj> obj2;
};
This is valid because in the const method, its just the pointer itself that becomes const, not the data it points to. However in many cases that is not what I desired and I want a compile error if a const method tries to change these members contents (either directly or by calling a non-const method on that member).
Is there a standard solution to this?
I think some kind of wrapper class should be able to achieve this, and should also be something the compiler optimises out, although haven't sat down to try and design such a thing to cover all cases giving say a strong_const<char*> str and strong_const<int&> (also not sure on a good name...).
Well, neither std::reference_wrapper nor std::shared_ptr do not provide const propagation, so they are not more "const-strict" than regular pointer.
I'd recommend to make your own const propagation class (I am not sure - maybe something similar is already provided by boost - please let me know in comments)
My proposition is this class:
#include <memory> // for pointer_traits
template <typename Pointer>
class ConstPropagatePointer
{
public:
using element_type = typename std::pointer_traits<Pointer>::element_type;
using pointer = typename std::pointer_traits<Pointer>::pointer;
using const_pointer = element_type const * const;
using reference = element_type&;
using const_reference = element_type const&;
ConstPropagatePointer(Pointer ptr) : ptr(ptr) {}
pointer operator -> ()
{
return &(*ptr);
}
const_pointer operator -> () const
{
return &(*ptr);
}
reference operator * ()
{
return *ptr;
}
const_reference operator * () const
{
return *ptr;
}
private:
Pointer ptr;
};
So that will work for you:
class Foo
{
public:
private:
ConstPropagatedPointer<char*> str;
ConstPropagatedPointer<ComplexObj*> obj;
ConstPropagatedPointer<std::shared_ptr<ComplexObj>> obj2;
};

How do I pass a variable to a function object?

In the examlpe below I need to define a function to compare my objects using certain rules in getHappiness(Animal*) method. The method cannot be static and rather complicated. I need a pointer in the comparison definition to call getHappiness method.
So my question is: how do I pass a pointer to this method, it gets called automatically when I insert an element into the map. And also it doesn't seem that I can instantiate Compare structure and pass the pointer to the constructor.
Am I doing anything wrong? Maybe there is an alternative way to how I define a comparison function?
struct Compare {bool operator()(Animal* const, Animal* const) const;};
bool
Compare::operator()(Animal* const a1, Animal* const a2) const {
Zoo* zoo; // somehow I need to get access to the Zoo instance here
if (zoo->getHappiness(a1) > zoo->getHappiness(a2)) return true;
return false;
}
Class Zoo(){
std::multimap<Animal*, Man*, Compare> map;
int getHappiness(Animal*); // cannot be static
}
int main(){
...
Zoo zoo;
zoo.map.insert(...);
...
}
There is a design issue in your code. Happiness should be an attribute which belong to an animal not a zoo. So implement getHappiness() on animal makes your code much simpler:
struct Compare
{
bool operator()(Animal& const, Animal& const) const;
};
bool Compare::operator()(Animal& const a1, Animal& const a2) const
{
return a1.getHappiness() < a2.getHappiness();
}
Class Zoo(){
std::multimap<Animal, Man, Compare> map;
}
Also, if not necessary, don't use pointer. If you can't avoid pointer, use smart pointer in STL container.

passing STL container in a class to another class

I am trying to pass a STL container in a class to another class
For ex.
typedef std::map<std::string, int*> Container_t;
class A
{
public:
const Container_t * get_container() const;
private:
Container_t container;
};
const Container_t * A::get_container() const
{
return &container;
}
And when I try to get this in another class, I get compilation error
saying
error: argument of type ‘Container_t* (A::)()const’ does not match ‘Container_t*’
void foo(A * a)
{
const Container_t * container = a->get_container();
}
It will be best if I can get const reference instead of pointer. but I don't want to copy the return value of function in A. So it has to be either pointer or reference. Thank you
You need to change your function signature to:
const Container_t * get_container() const;
The reason is that the last const there at the end says "calling this function and using what it returns will not change this object." However, returning a (non-const) pointer to the member variable gives the caller freedom to change that variable, so you have to return a const Container_t * to ensure that the caller doesn't modify the member variable (and thereby modify the object).

const and pointers

Edit1: I realize this is hard to understand this question without having an insight of what I'm trying to do. The class A is not complete but it essentially stand for a C-array "proxy" (or "viewer" or "sampler"). One interesting usage is too present a C-array as a 2d grid (the relevant function are not shown here). The property of this class are the following:
it should not own the data - no deep copyy
it should be copyable/assignable
it should be lightweight (
it should preserve constness (I'm having trouble with this one)
Please do not question the purpose or the design: they are the hypothesis of the question.
First some code:
class A
{
private:
float* m_pt;
public:
A(float* pt)
:m_pt(pt)
{}
const float* get() const
{
return m_pt;
}
void set(float pt)
{
*m_pt = pt;
}
};
void gfi()
{
float value = 1.0f;
const A ac(&value);
std::cout<<(*ac.get())<<std::endl;
A a = ac;
a.set(2.0f);
std::cout<<(*ac.get())<<std::endl;
}
Calling "gfi" generate the following output:
1
2
Assigning a with ac is a cheap way to shortcut the constness of ac.
Is there a better way to protect the value which m_pt point at?
Note that I DO want my class to be copyable/assignable, I just don't want it to loose its constness in the process.
Edit0: I also DO want to have a pointer in there, and no deep copy please (let say the pointer can be a gigantic array).
Edit2: thanks to the answers, I came to the conclusion that a "const constructor" would be a useful thing to have (at least in this context). I looked it up and of course I'm not the same one who reached this conclusion. Here's an interesting discussion:
http://www.rhinocerus.net/forum/language-c-moderated/569757-const-constructor.html
Edit3: Finally got something which I'm happy with. Thanks for your help. Further feedback is more than welcome
template<typename T>
class proxy
{
public:
typedef T elem_t;
typedef typename boost::remove_const<T>::type elem_unconst_t;
typedef typename boost::add_const<T>::type elem_const_t;
public:
elem_t* m_data;
public:
proxy(elem_t* data = 0)
:m_data(data)
{}
operator proxy<elem_const_t>()
{
return proxy<elem_const_t>(m_data);
}
}; // end of class proxy
void test()
{
int i = 3;
proxy<int> a(&i);
proxy<int> b(&i);
proxy<const int> ac(&i);
proxy<const int> bc(&i);
proxy<const int> cc = a;
a=b;
ac=bc;
ac=a;
//a=ac; // error C2679: binary '=' : no operator found which takes a right-hand operand of type...
//ac.m_data[0]=2; // error C3892: 'ac' : you cannot assign to a variable that is const
a.m_data[0]=2;
}
Your class is badly designed:
it should use float values, not pointers
if you want to use pointers, you probably need to allocate them dynamically
and then you need to give your class a copy constructor and assignment operator (and a destructor) , which will solve the problem
Alternatively, you should prevent copying and assignment by making the copy constructor and assignment op private and then not implementing them.
You can trick around with proxy pattern and additional run-time constness boolean member. But first, please tell us why.
Effectively your class is like an iterator that can only see one value. It does not encapsulate your data just points to it.
The problem you are facing has been solved for iterators you should read some documentation on creating your own iterator and const_iterator pairs to see how to do this.
Note: in general a const iterator is an iterator that cannot be incremented/decremented but can change the value it points to. Where as a const_iterator is a different class that can be incremented/decremented but the value it points to cannot be changed.
This is the same as the difference between const float * and float *const. In your case A is the same as float * and const A is the same as float *const.
To me your choices seem to be:
Encapsulate your data.
Create a separate const_A class like iterators do
Create your own copy constructor that does not allow copies of const A eg with a signature of A(A & a);
EDIT: considering this question some more, I think you are misinterpreting the effect of const-correctness on member pointers. Consider the following surprising example:
//--------------------------------------------------------------------------------
class CNotSoConstPointer
{
float *mp_value;
public:
CNotSoConstPointer(float *ip_value) : mp_value(ip_value) {}
void ModifyWithConst(float i_value) const
{
mp_value[0] = i_value;
}
float GetValue() const
{
return mp_value[0];
}
};
//--------------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
float value = 12;
const CNotSoConstPointer b(&value);
std::cout << b.GetValue() << std::endl;
b.ModifyWithConst(15);
std::cout << b.GetValue() << std::endl;
while(!_kbhit()) {}
return 0;
}
This will output 12 and then 15, without ever being "clever" about the const-correctness of the const not-so-const object. The reason is that only the pointer ITSELF is const, not the memory it points to.
If the latter is what you want, you'll need a lot more wrapping to get the behavior you want, like in my original suggestion below or Iain suggestion.
ORIGINAL ANSWER:
You could create a template for your array-proxy, specialized on const-arrays for the const version. The specialized version would have a const *m_pt, return a const pointer, throw an error when you try to set, and so on.
Edit: Something like this:
template<typename T>
class TProxy
{
T m_value;
public:
TProxy(T i_t) : m_value(i_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.m_value) {}
T get() { return m_value; }
void set(T i_t) { m_value = i_t; }
};
template<typename T>
class TProxy<const T *>
{
const T *mp_value;
public:
TProxy(const T *ip_t) : mp_value(ip_t) {};
template<typename T>
TProxy(const TProxy<T> &i_rhs) : m_value(i_rhs.mp_value) {}
T get() { return m_value; }
};
Why not replace float* with float in A. If you don't either the original owner of the float that the float* references can change it, or anyone prepared to do a mutable cast on the return value from a::get.
const is always just a hint to the compiler; there are no ways to make a variable permanently read-only.
I think you should use deep copy and define your own assingment operator and copy constructor.
Also to return handle to internal data structure in not a good practice.
You can deny the copy-constructor for certain combinations of arguments:
For instance, adding the constructor;
A(A& a) :m_pt(a.m_pt) { m_pt = a.m_pt; }
prevents any instance of A being initialised with a const A.
This also prevents const A a2 = a1 where a1 is const, but you should never need to do this anyway, since you can just use a1 directly - it's const even if you could make a copy, a2 would be forever identical to a1.