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.
Related
Accordingly to C++ best practices on IsoCpp we shouldn't have a const or reference data member: C.12: Don’t make data members const or references
But it does not specify if a pointer to a const object is allowed or not. Consider the example:
class Connection {
gsl::not_null<const Network*> m_network;
...
public:
[[nodiscard]] const Network* getNetwork() const;
...
}
// Implementation
const Network* Connection::getNetwork() const {
return m_network;
}
Is it still conformant with the best practice described? The pointer isn't const but the data the pointer points to is.
Should we remove const from member declaration but still mark the getter return type as a pointer to const?
It depends.
The reason this recommendation exists, is to make your type copyable and assignable, with both having matching semantics.
So consider this:
struct Connection {
Network const& network;
/**/
};
Copying this is perfectly fine, right? But then assigning breaks because network will not be reseatable.
So in this case, replacing that member with an e.g. std::shared_ptr<Network const> is fine. Presumably several connections can use the same network.
But -- if you do not want sharing but each object to have its own copy of the member, then you are left with reconstructing that member on assignment, because the old object cannot be changed. Depending on the object that might not be what you want to do.
For example:
struct Connection {
std::shared_ptr<Network const> network;
std::unique_ptr<std::vector<Entry> const> properties;
};
Aside from a pointer-to-vector being bad form (double indirection for no good reason). Reconstructing a vector anew requires a new memory allocation, while just reusing the old will not (assuming there was enough memory allocated).
Granted, this is a bit of constructed example, and there would maybe be a way to go around that. But the point is, that the similarty of your copy and assignment will heavily depend on the quality of the copy and assignment of the member object.
In such a case the same reasoning that applied to the situation without the pointer similarly applies to the situation with the pointer. You will have to decide whether that is acceptable for your type.
However, I hold that not every type must be assignable. You can make the conscious decision to have a type that just cannot be assigned to. Then and only then, just have a const member without any indirection.
Yes, you can
If you have a const data member, the copy and move constructor are deleted. But this only applies if the pointer itself is const, not the pointed value.
const & or const * are only fine when the class is uncopyable (like a "manager class" of some sort)
Are smart pointers considered as pointers? And thus can they implicitly used as pointers?
Let's say I have the following class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
Then can I use MyClass the following way?
// m is an instance of MyClass
m.bar(m.foo());
No they can't be used interchangable. You would get a compiler error in your example. But you can always get the raw pointer by shared_ptr::get().
NO! It would be a terrible API. Yes, you could easily implement it within shared_ptr, but just because you could doesn't mean you should.
Why is it such a bad idea? The plain-pointer-based interface of bar doesn't retain an instance of the shared pointer. If bar happens to store the raw pointer somewhere and then exit, there's nothing that guarantees that the pointer it had stored won't become dangling in the future. The only way to guarantee that would be to retain an instance of the shared pointer, not the raw pointer (that's the whole point of shared_ptr!).
It gets worse: the following code is undefined behavior if foo() returns a pointer instance that had only one reference when foo() returned (e.g. if foo is a simple factory of new objects):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
Here are the options; consider those listed earlier first before considering their successors.
If bar(AnotherClass *) is an external API, then you need to wrap it in a safe way, i.e. the code that would have called Original::bar should be calling MyWrapped::bar, and the wrapper should do whatever lifetime management is necessary. Suppose that there is startUsing(AnotherClass *) and finishUsing(AnotherClass *), and the code expects the pointer to remain valid between startUsing and finishUsing. Your wrapper would be:
class WithUsing {
std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
std::shared_ptr<User> user;
public:
WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
owner(std::move(owner)), user(std::move(user)) {
user.startUsing(owner.get());
}
void bar() const {
user.bar(owner.get());
}
~WithUsing() {
user.finishUsing(owner.get());
}
};
You would then use WithUsing as a handle to the User object, and any uses would be done through that handle, ensuring the existence of the object.
If AnotherClass is copyable and is very cheap to copy (e.g. it consists of a pointer or two), then pass it by value:
void bar(AnotherClass)
If the implementation of bar doesn't need to change the value, it can be defined to take a const-value (the declaration can be without the const as it doesn't matter there):
void bar(const AnotherClass a) { ... }
If bar doesn't store a pointer, then don't pass it a pointer: pass a const reference by default, or a non-const reference if necessary.
void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
If it makes sense to invoke bar with "no object" (a.k.a. "null"), then:
If passing AnotherClass by value is OK, then use std::optional:
void bar(std::optional<AnotherClass> a);
Otherwise, if AnotherClass takes ownership, passing unique_ptr works fine since it can be null.
Otherwise, passing shared_ptr works fine since it can be null.
If foo() creates a new object (vs. returning an object that exists already), it should be returning unique_ptr anyway, not a shared_ptr. Factory functions should be returning unique pointers: that's idiomatic C++. Doing otherwise is confusing, since returning a shared_ptr is meant to express existing shared ownership.
std::unique_ptr<AnotherClass> foo();
If bar should take ownership of the value, then it should be accepting a unique pointer - that's the idiom for "I'm taking over managing the lifetime of that object":
void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
If bar should retain shared ownership, then it should be taking shared_ptr, and you will be immediately converting the unique_ptr returned from foo() to a shared one:
struct MyClass {
std::unique_ptr<AnotherClass> foo();
void bar(std::shared_ptr<const AnotherClass> a);
void bar_modifies(std::shared_ptr<AnotherClass> a);
};
void test() {
MyClass m;
std::shared_ptr<AnotherClass> p{foo()};
m.bar(p);
}
shared_ptr(const Type) and shared_ptr(Type) will share the ownership,
they provide a constant view and a modifiable view of the object, respectively. shared_ptr<Foo> is also convertible to shared_ptr<const Foo> (but not the other way round, you'd use const_pointer_cast for that (with caution). You should always default to accessing objects as constants, and only working with non-constant types when there's an explicit need for it.
If a method doesn't modify something, make it self-document that fact by having it accept a reference/pointer to const something instead.
Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced).
Smart pointer are there to manage lifetime of the pointer they own/share.
You can think of a wrapper that has a pointer inside. So the answer is no. However you can access to the pointer they own via get() method.
Please note that it is not so difficult to make dangling pointers if you use get method, so if you use it be extra cautious.
I have a member function which is declared const and modifies data via a pointer. This seems misleading. Should I remove the const keyword?
I would like to know how others handle this situation in their code. Do people just add a comment to clarify what is going on? Do they not add the const keyword to the member function? Maybe something else completely?
Any advice is appreciated.
You have essentially two choices:
Deep constness:
class Foo
{
T * ptr;
public:
T & operator*() { return *ptr; }
T const & operator*() const { return *ptr; }
T * operator->() { return ptr; }
T const * operator->() const { return ptr; }
};
Shallow constness:
class Foo
{
T * ptr;
public:
T & operator*() const { return *ptr; }
T * operator->() const { return ptr; }
};
It's really up to you, and to the purpose of your class. If the class is a smart pointer, it would seem reasonable to have shallow constness semantics, since the class is supposed to be as similar to a raw pointer as possible (and you can of course have a constant raw pointer to a non-constant pointee).
Otherwise, you should ask yourself why you would be exposing access to a member pointer object at all. It's certainly possible that you want to give mutable access via constant references to your class, but I imagine those are special and rare circumstances. There shouldn't really be that many raw pointers in your code in the first place. Returning a deeply-const reference by dereferencing a pointer should be fine, but usually in better encapsulated "getter" functions which hide the fact that there is a pointer inside your class, like T const & get() const { return *ptr; }.
Generally, yes. Its deceptive to modify something you are declaring constant, even though you can do it.
If someone uses your code and sees const, they expect const. Modification, even though sensible to you, might cause them severe problems -- even crashing a program.
Consider a std::vector<Blah> member versus a Blah* member used to implement a dynamic array. Most often it makes sense to replace the latter with the former. With the Blah* memeber a const method is allowed to modify the data in the array, while with the std::vector<Blah> member the const method is not allowed to modify data there.
Also consider a matrix class with an indexing method that returns a proxy that allows assignment to an element. Assigning via the proxy changes the matrix, not the proxy object itself. Thus, the proxy object’s assignment operator can be (and should be) const, in order to impose the most constraints possible on its effect, while its primary job is to modify things.
That’s another example that the design level is different from the coding level.
In the first example, with a member array, const was all about expressing a design level constraint, but in the second example, with the assignment proxy, const was all about expressing a coding level constraint.
These usages are not incompatible, however. The key idea is to provide a reader of the code with as many constraints as possible (because that greatly reduces how many varying things that must be considered to understand or deal with the code). Upshot: add const wherever you practically can.
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.
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).