I'm a little confuse about meaning of this const keyword
I have a class like this
class ClassA {
public:
typedef std::tr1::shared_ptr<ClassA> ptr;
typedef std::tr1::shared_ptr<const ClassA> const_ptr;
void oper() const;
void oper();
private:
.....
};
int main()
{
std::list<ClassA::const_ptr> const_list;
.....
for(std::list<ClassA::const_ptr>::iterator it = const_list.begin();\
it != const_list.end(); it++)
{
(*it)->oper();
}
return 0;
}
I already get const version of oper() from the code above. So I can't imagine what will I get if I change std::list::iterator to std::list::const_iterator.
Your situation is a bit confusing because there are two levels of indirection (the iterator and the smart pointer), with const being applicable in some way to any of them (and also to the referenced object).
You can apply const:
to the object itself; this means that it cannot be modified;
to the smart pointer; this means that the smart pointer cannot be modified, e.g. cannot be reseated via reset;
in some sense to the iterator, using a const_iterator; this means that it will yield a const reference to the object it refers (=>the smart pointer) and that it cannot be used to modify the sequence it refers to.
Expanding a little:
Remember that a const shared_ptr<const ClassA>& (which is what you get by dereferencing a const_iterator) is different from a shared_ptr<const ClassA>& (which you get from a normal iterator): although on both you cannot modify the pointed object (due to the fact that shared_ptr refers to a const ClassA), on the const one you cannot modify the shared_ptr itself, which e.g. means that you can't reset it to point to another object, you cannot assign another shared_ptr to it, ...
Remember also that const versions of iterators, beside yielding a const reference to what they refer to, also disallow modifying the container through them (e.g. you cannot erase an element via a const_iterator).
Not sure if you understand what void oper() const in class ClassA means: In particular, it means that means is that ClassA::oper() is not allowed to modify any members of ClassA.
Which has little baring on your choice of iterator or const_iterator, that choice would have different implications.
Related
I understand that std::unique_ptr is the way it is and probably won't be changed to break backwards compatibility but I was wondering if anyone has a good reason why the writers of the spec didn't overload the get method with a const variant that looks like
const T* get() const;
to follow the intent of the unique_ptr being const.
My best guess is that it is trying to mirror pointers and act like a T* const instead of a typical class. As a follow-up question, if I wanted to hold a pointer in a const-like fashion in a const instance of my class, should I be using something else other than std::unique_ptr to hold the data?
Update
In my case I want to protect myself from misusing the pointer in the class itself. I was writing a const move constructor MyClass(const MyClass&& other) and was copying the data from the new instance into other via std::copy. It took a long time to track down the bug because I had assumed the copy must be correct because of const protection. I'm trying to figure out what I could have done to protect myself from this outside of providing a const getter and using that within the class when doing the copy.
Smart pointers are pretending to be a raw pointer.
If you have class member which is raw pointer and use it in const method that you can't update a pointer, but you can modify object which is pointed.
Same behavior is desired for smart pointer. So std::unique_ptr::get is a const method, but doesn't force to return pointer to const object.
Note also that you can have a pointer to const object.
MyClass *pointerToObject
std::unique_ptr<MyClass> smartPointerToObject;
// but you can have also a case
const MyClass *pointerToConstObject
std::unique_ptr<const MyClass> smartPointerToConstObject;
In last case std::unique_ptr::get will return something you are expecting.
Based on comment below:
Just provide private methods:
InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }
And use it in your code and you will have const object of inner class in const method.
There's no point to giving read-only access to an object via its unique_ptr. You only pass unique_ptr around when you are transferring ownership, for access to the object without an ownership transfer, call up.get() and pass a const T* to the function that should only read (or if the pointer is never nullptr, it's also reasonable to evaluate *(up.get()) and pass a const T&).
As a bonus, this allows you to use that function with objects stored on the stack, embedded inside another object, or managed with a smart pointer other than unique_ptr.
There's a good discussion of all the unique_ptr parameter passing cases (in/out, const/non-const, etc) here:
How do I pass a unique_ptr argument to a constructor or a function?
For the same reason a T*const when dereferenced is a T&, not a T const&.
Constness of pointer is distinct from pointness of pointed-to.
get is const, it does not modify the state of unique_ptr.
Its constness does not impact the constness of the contents.
There is the idea of smart pointers that propogate constness, but unique_ptr is not that beast.
std::experimental::propogate_const wraps a pointer-like object and makes const travel through it.
It, or something like it, may solve your problem.
Note that I find half the time when I try to have const propogate like this, I discover I was wrong. But this may not be the case here.
In general, the proper way to handle the guts of a T*const in a const manner is to pass a T const& (or the nullable variant T const*).
I think this concern is valid,
there should be 2 versions for each de-referencing functions,
e.g.
const T* get() const;
T* get();
enter code here
I know that purpose of providing "T* get() const" is to ease replace existing raw pointer usages.
But since uniq ptr denotes ownership, it is incorrect that some one being able to modify some thing OWNED by the object via a immutable(const) reference [assuming modifying something fully owned by a object is same as modifying the object itself - which is true if this was an object instead of a ptr].
May be best option would be std to provide another version of Uniq ptr which holds to above idiom (only other option may be to derive a new class from uniq ptr and provide 2 versions for de-referencing )
Because as far as the unique_ptr is concerned, getting the internal raw pointer reference is a const operation. Calling .get() and retrieving the internal raw pointer of a std::unique_ptr does not change the internal state of the std::unique_ptr object itself. So it seems the library designers elected to mark it const without attention to what could happen to the underlying object if they just return a straight non-const reference to it.
In fact, if you have a std::unique_ptr inside an object, and you call a const member function of that object, you can still call non-const member functions on the internal std::unique_ptr inside that object. For example:
struct A {
void Const() const { }
void nonConst() { }
};
struct B {
std::unique_ptr<A> a;
void go() const {
a->nonConst(); // OK
}
};
Although you cannot perform non-const operations on the internal state variables of an object from one of its const member function, there is no rule that says you cannot perform non-const operations on other objects.
What you may be expecting is the constness promise to carry over from the unique_ptr to also apply to access to what it internally points to, so you'd expect unique_ptr to be written something like this:
template <typename T>
class cunique_ptr {
T* ptr;
public:
cunique_ptr() {
ptr = new T;
}
~cunique_ptr() {
delete ptr;
}
// You can only get a non-const pointer to the internals from a non-const object
T* get() { return ptr; }
// The const member function carries over the const promise to access to its internals
const T* get() const { return ptr; }
};
void test() {
cunique_ptr<A> a;
a.get()->nonConst();
const cunique_ptr<A> ca;
//ca.get()->nonConst(); //X fails: cannot call non-const member functions from const object
ca.get()->Const();
}
However, it seems the library designers elected against that type of protection and let the const promise be kind of shallow as it were.
It makes sense in my head, but I'm not sure if this is proper const-correctness.
I have a container like this:
template<typename T>
class IDMap
{
public:
typedef uint64_t ItemID;
T& GetItem(const ItemID id) const
{
const uint32_t index = _IDSTORAGE_INDEX_MASK(id);
const uint32_t version = _IDSTORAGE_VERSION_MASK(id);
assert(mItems[index].mVersion == version);
return mItems[index].mItem;
}
// ....
};
I can delegate const references to this container to other classes knowing full well they cannot modify the container, but they can access and modify it's objects.
Is this a valid case? Valid as in "This solves a specific problem for me, but I'm not sure if its good practice"
It depends.
Const correctness gets pretty subtle when it comes to references held by objects. If you have a dynamic array class, with a pointer to the current array of values, technically you can modify those pointed-to values from within a const function... but you probably shouldn't, because that means the const function is changing "the dynamic array object", even if the object itself isn't changing. The array owns those values, and their constness should be protected by its own.
But if your object is not the owner of the pointed-to objects, then giving out non-const references to them is generally the right thing to do. In that case, the constness of your object extends only to the identity of the objects in the mapping, not what values are inside of them.
It's not usually considered const-correct. However, the simple fact is that the language does not really permit a correct use of const in this scenario. For example, you can't say std::vector<const T>. Even if you fixed your IDMap to support such usage, it still wouldn't support things like covariance on constness.
Your choices are one bad use of const or another bad use of const, so take your pick.
What's more concerning is this _IDSTORAGE_VERSION_MASK. Using _U-style names in user code is undefined behaviour.
For fun and profitâ„¢, I'm writing a trie class in C++ (using the C++11 standard.)
My trie<T> has an iterator, trie<T>::iterator. (They're all actually functionally const_iterators, because you cannot modify a trie's value_type.) The iterator's class declaration looks partially like this:
template<typename T>
class trie<T>::iterator : public std::iterator<std::bidirectional_iterator_tag, T> {
friend class trie<T>;
struct state {
state(const trie<T>* const node, const typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator& node_map_it ) :
node{node}, node_map_it{node_map_it} {}
// This pointer is to const data:
const trie<T>* node;
typename std::vector<std::pair<typename T::value_type, std::unique_ptr<trie<T>>>>::const_iterator node_map_it;
};
public:
typedef const T value_type;
iterator() =default;
iterator(const trie<T>* node) {
parents.emplace(node, node->children.cbegin());
// ...
}
// ...
private:
std::stack<state> parents;
// ...
};
Notice that the node pointer is declared const. This is because (in my mind) the iterator should not be modifying the node that it points to; it is just an iterator.
Now, elsewhere in my main trie<T> class, I have an erase function that has a common STL signature--it takes an iterator to data to erase (and returns an iterator to the next object).
template<typename T>
typename trie<T>::iterator trie<T>::erase(const_iterator it)
{
// ...
// Cannot modify a const object!
it.parents.top().node->is_leaf = false;
// ...
}
The compiler complains because the node pointer is read-only! The erase function definitely should modify the trie that the iterator points to, even though the iterator shouldn't.
So, I have two questions:
Should iterator's constructors be public? trie<T> has the necessary begin() and end() members, and of course trie<T>::iterator and trie<T> are mutual friends, but I don't know what the convention is. Making them private would solve a lot of the angst I'm having about removing the const "promise" from the iterator's constructor.
What are the correct const semantics/conventions regarding the iterator and its node pointer here? Nobody has ever explained this to me, and I can't find any tutorials or articles on the Web. This is probably the more important question, but it does require a good deal of planning and proper implementation. I suppose it could be circumvented by just implementing 1, but it's the principle of the thing!
1) All iterators are required to be copy-constructible. Your iterator is Bi-directional, hence is also required to be default-constructible (http://en.cppreference.com/w/cpp/concept/ForwardIterator), although I don't know why. So the default constructor needs to be public but you can do what you like with the const trie<T>* one. I would think it should be private, since the purpose of this class is to provide the user with an iterator over the trie, and so its public interface should be only that of an iterator of the appropriate category. No need for any extra public constructors.
2) erase is a non-const function. The only iterators you can validly pass to it are iterators that refer to the same trie that the function is called on, which means (I think, although I'm not quite certain I've followed your design) the whole hierarchy of parents are non-const objects. So I suspect this is one of those cases where you can const_cast<trie<T>*>(it.parents.top().node). The iterator isn't allowed to use it to modify the trie, which is why you want it to hold a pointer-to-const. But when you hold a non-const pointer to the trie, namely this, you are allowed to modify any part of it you like, and the iterator is just giving you the position to start modifying from.
There might be some more general principle of const-safety you can draw here. One possible case in container::erase(const_iterator) functions is that the const container* you'd get from the iterator is equal to this. In that case the const_cast is certainly both safe and legitimate (as well as unnecessary, because you can just use this, but that's beside the point of whether it's const-correct or not). In your container it is not (in general) equal to this, it points to one of the several trie objects that together make up the hierarchical container that this is part of. The good news is, that whole container is logically const or logically non-const together, hence the const_cast is just as safe and legitimate as if it were all one object. But a bit harder to prove correct, because you have to make sure that in your design the whole hierarchical container genuinely does, as I've assumed, share non-constness.
Non-const methods of trie that wish to modify what a const_iterator points to (what it points to should be within this instance of trie) should just const_cast as needed. You know this is a safe and defined cast because if someone managed to call a non-const instance of this trie, then this instance of trie itself is not const.*
Alternatively you could do the opposite and hold non-const pointer(s) to the trie within the const_iterator, which will require a const_cast upon construction. This is safe for the same reason, above. The const_iterator methods will only provide const access, so the user can't mutate the part(s) of trie that the iterator points to. And if a const_iterator needs to be mutated in a non-const trie method it's okay because the trie must not have been const in the first place.*
The premise of the safety of const_casting here is that it is safe to hold and use a non-const pointer to a const object so long as you do not mutate that object. And it is safe to turn a cast away the constness of a pointer which points to something which wasn't originally declared as const.
*Yes, the caller of the non-const trie method might have const_casted in an undefined way; but in that case the burdon of causing undefined behavior is on their head, not tries.
Should iterator's constructors be public?
At least the copy constructor, yes. Have a look at this chart that describes the traits each type of iterator should have:
http://www.cplusplus.com/reference/iterator/
All iterator types should be copy-constructible, copy-assignable and destructible, so that means they need public copy constructors. Some iterators, for example the RandomAccessIterator should also be default constructible so the default constructor should be public as well.
What are the correct const semantics/conventions regarding the iterator and its node pointer here?
If you want to have an erase then you don't really have a const_iterator, you have a regular iterator. The difference between the two is that if you have a const trie object then you can only get a const_iterator out of it because you're not allowed to modify it in any way.
You can notice this in the STL containers. They tend to have both:
iterator begin();
const_iterator begin() const;
What usually happens is that you implement a const_iterator then:
class iterator : public const_iterator {...};
which implements the one or two non-const functions. This probably means just erase for you, since your operator* is going to stay const.
Consider the following code:
#include <vector>
using namespace std;
struct foo
{
void bar()
{
}
};
int main()
{
{
vector<foo*> a;
a.push_back(new foo());
a.push_back(new foo());
a.push_back(new foo());
vector<foo*>::const_iterator itr = a.begin();
(*itr)->bar(); // compiles - this becomes more confusing
// when found in a const method. On first
// glance, one will (or at least me) may
// assume that bar() must be const if the
// method where it is being called from is
// const
// The above compiles because internally, this is what happens
// (ignore the fact that the pointer has not been newd)
foo* const * element;
(*element)->bar(); // compiles
// What I would expect however (maybe it is just me) is for const_iterator
// to behave something like this
const foo* const_element;
const_element->bar(); // compile error
}
{
vector<foo> a;
a.resize(10);
vector<foo>::const_iterator itr = a.begin();
itr->bar(); // compile error
}
}
I understand why it can be called. The const_iterator stores the const-ness like this: const T* which for the pointer translates to foo* const * and for the object foo const *.
So my question is, why are we allowed to call a non-const member function from a const_iterator? Isn't it more intuitive to not allow the call to a non-const member function from a const_iterator? Shouldn't the design of iterators with the const option prevent this behaviour?
The more important question now is this: What if I want const_iterator to disallow calling of non-const member functions of the pointed to object?
Shouldn't the design of iterators with the const option prevent this behaviour?
It does. You're just expecting it to be a different operation.
As you discovered, a container of pointers... contains pointers, not objects. Therefore, a const_iterator to such pointers means that the pointers are constant, not the objects they point to.
That's not going to change, nor should it. The standard library containers are generally designed to contain full-fledged objects, not pointers. So they shouldn't encourage users to make vectors of pointers and other dubious constructs.
If you really need a vector to contain pointers, then you should use a container that is actually designed to do so. Like Boost's pointer container classes. Their const_iterators make the objects being pointed to const properly. They also do other useful things, like own the objects they point to (so that they are properly deleted) and so forth.
You have a vector of pointers, pointers don't have member functions so you're not calling a member function on something that is stored in the vector.
The type of object that you get when you dereference a pointer depends on the type of that pointer. Your vector is a vector of non-const pointers so when you derefence any pointer from your container you always get a non-const reference to the pointed to object.
If you want a vector of pointers then you have two options. You can create a vector<const foo*> instead and you will never be able to retrieve a non-const reference to any pointer to object or, if you need to be able to get non-const references from non-const instances of your vector you will have to create an object that contains the vector as a private member variable and gives you the access that you want via a pass-through interface.
If your vector is supposed to own the objects that it holds pointers to you could consider simple vector<foo> instead or, if dynamic allocation is required, a boost::ptr_vector<foo>.
So my question is, why are we allowed to call a non-const member function from a const_iterator?
"Constant iterator" means container element is constant. It doesn't mean that if that element is a pointer, the pointed object is also constant.
What if I want const_iterator to disallow calling of non-const member functions of the pointed to object?
I wouldn't want to encourage you using raw pointers like this, but if you really have to, make it a container of pointers to const objects.
std::vector<const foo*> a;
// ...
auto itr = a.cbegin();
(*itr)->bar(); // Compiler error (calling a non-const method on const object).
Of course, in that case the following would also be disallowed:
std::vector<const foo*> a;
// ...
auto itr = a.begin();
(*itr)->bar(); // Compiler error.
i have my own class "SomeObject" with a few members.
now, i have another class "WorkingClass" containg this object as privat member.
My Question is: i want to create a Getter for the "SomeObject", but i don't want anyone to modify it.
which way is better, 1 or 2?
class WorkingClass
{
private:
SomeObject sObj;
public:
//... 1)
const SomeObject &const GetSomeObject()
{
return mousePosition;
}
//... 2)
const SomeObject *const GetSomeObject()
{
return &mouseMovement;
}
}
i know you can always cast away const, but still, i'm just trying to get my code clean and fail-safe
EDIT:
then i have a further question. when i have a smart-pointer member and use it a lot inside the class, and then suddenly want someone to have acces to read some values but nothing more, would this be a good solution or is that verbose again?
class X
{
private:
boost::shared_ptr<SomeObject> sob
public:
const const & GetSomeObject()
{
return *sob.get();
}
}
and how about returning a "const boost::shared_ptr<...> GetX()" ? it may not be really neccessary, but still not useless, as the compiler would forbid GetX().reset(..) in such a case, and without the const boost::... declaration this useless operation would be permitted. or am i wrong?
Neither is good:
const SomeObject &const is ill-formed. You cannot const-qualify a reference. (You can, of course, qualify the referent type.)
const SomeObject *const is unnecessarily verbose. A function call expression o.GetSomeObject() is an rvalue expression and only class-type rvalues can be const-qualified. You may as well just say const SomeObject*. (const SomeObject *const can actually lead to issues with template instantiation, though such issues are rare.)
As for whether you choose to return by pointer or by reference, it depends on how you are using the return value. Both can make sense in different circumstances. Regardless, you want to return a reference or pointer to a const object, not a const reference or a const pointer:
const SomeObject& GetSomeObject() { ... }
const SomeObject* GetSomeObject() { ... }
Usually, returning a reference is preferable.
Writing & const is pointless. References are always constant. Omit the const keyword there.
Using a reference is better if you want to return a constant non-null object. If you want to return either an object or a null pointer then use a pointer instead.
See also When should I use references, and when should I use pointers?
Use references when you can, and pointers when you have to.
References are usually preferred over pointers whenever you don't need "reseating". This usually means that references are most useful in a class's public interface. References typically appear on the skin of an object, and pointers on the inside.
The exception to the above is where a function's parameter or return value needs a "sentinel" reference — a reference that does not refer to an object. This is usually best done by returning/taking a pointer, and giving the NULL pointer this special significance (references should always alias objects, not a dereferenced NULL pointer).