I have a pointer to a list of pointers, as a private variable. I also have a getter that returns the pointer to the list. I need to protect it from changes.
I couldn't find how to use reinterpret_cast or const_cast on this.
class typeA{
shared_ptr<list<shared_ptr<typeB>>> l;
public:
shared_ptr<list<shared_ptr<const typeB>>> getList(){return (l);};
};
The compiler returns:
error: could not convert ‘((typeA*)this)->typeA::x’ from ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<typeB> > >’ to ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<const typeB> > >’|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
It seems as const shared_ptr<list<shared_ptr<typeB>>>
and shared_ptr<const list<shared_ptr<typeB>>>
work fine.
Is it possible to do return l
as a complete const, like:
const shared_ptr<const list<shared_ptr<const typeB>>>
or at least like:
shared_ptr<list<shared_ptr<const typeB>>>
?
References instead of pointers is not an option. To declare l
as shared_ptr<list<shared_ptr<const typeB>>>
also is not a wanted solution.
EDIT: no 'int' anymore.
It seems as it is not possible exactly what I wanted, but the suggested solutions are good. Yes, copying pointers is acceptable.
My bad i didn't put typeB immediately. I am aware of some advantages of references over pointers, but I hoped there is some similar solution.
You can create a new list of
const int
's from your original list and return that:If you want to prevent people from making changes to the returned list, make it const too:
The shared pointer returned by this function does not point to the original list but to the newly created list.
An alternative may be to provide iterators that, when dereferenced, returns
const T&
(where T is the type you actually store). That way there will be no need to copy the whole list every time you want to go though it. Example:You problem squarely lies at
What you are finding here is that statement is wrong. A
list<TypeB>
can bind aconst list<TypeB> &
reference, and none of the list's members will allow any modification of theTypeB
objects.If you really really must have
const typeB
, you could instead return a projection ofl
that has addedconst
, but that wouldn't be a Container, but instead a Range (using theranges
library voted into C++20, see also its standalone implementation)Another alternative is that you can wrap your
std::shared_ptr
s in something likestd::experimental::propagate_const
, and just directly return them.The problem with templates is that for any
any two pairs
C<TypeA>
andC<TypeB>
are totally unrelated classes – this is even the case ifTypeA
andTypeB
only differ inconst
-ness.So what you actually want to have is technically not possible. I won't present a new workaround for now, as there are already, but try to look a bit further: As denoted in comments already, you might be facing a XY problem.
Question is: What would a user do with such a list? She/he might be iterating over it – or access single elements. Then why not make your entire class look/behave like a list?
If you used a vector instead of a list, I'd yet provide an index operator:
Now one problem yet remains unsolved so far: The two
const_iterators
returned have an immutable shared pointer, but the pointee is still mutable!This is a bit of trouble - you'll need to implement your own iterator class now:
Have a look at
std::iterator
for a full example – be aware, though, thatstd::iterator
is deprecated, so you'll need to implement the type-traits yourself.The iterator tag to be used would be
std::bidirectional_iterator_tag
orrandom_access_iterator_tag
(contiguous_iterator_tag
with C++20), if you use astd::vector
inside.Now important is how you implement two of the needed operators:
The other operators would just forward the operation to the internal iterators (increment, decrement if available, comparison, etc).
I do not claim this is the Holy Grail, the path you need to follow under all circumstances. But it is a valuable alternative worth to at least consider...
When you convert a pointer-to-nonconst to a pointer-to-const, you have two pointers. Furthermore, a list of pointers-to-nonconst is a completely different type from a list of pointers-to-const.
Thus, if you want to return a pointer to a list of pointers-to-const, what you must have is a list of pointers-to-const. But you don't have such list. You have a list of pointers-to-nonconst and those list types are not interconvertible.
Of course, you could transform your pointers-to-nonconst into a list of pointers-to-const, but you must understand that it is a separate list. A pointer to former type cannot point to the latter.
So, here is an example to transform the list (I didn't test, may contain typos or mistakes):
Since we have created a completely new list, which cannot be pointed by
l
, it makes little sense to return a pointer. Let us return the list itself. The caller can wrap the list in shared ownership if the need it, but don't have to when it is against their needs:Note that while the list is separate, the pointers inside still point to the same integers.
As a side note, please consider whether shared ownership of an
int
object makes sense for your program - I'm assuming it is a simplification for the example.Also reconsider whether "References instead of pointers is not an option" is a sensible requirement.
What you have here is a VERY complex construct:
This is three levels of indirection, of which two have reference counting lifetime management, and the third is a container (and not memory-contiguous at that).
Naturally, given this complex structure, it is not going to be easy to convert it to another type:
Notice that
std::list<A>
andstd::list<const A>
are two distinct types by design of standard library. When you want to pass around non-modifying handles to your containers, you are usually supposed to useconst_iterator
s.In your case there is a
shared_ptr
on top of thelist
, so you can't use iterators if you want that reference counting behavior.At this point comes the question: do you REALLY want that behavior?
typeA
instance is destroyed, but you still have some othertypeA
instances with the same container?typeA
instances sharing the container are destroyed, but you still have some references to that container in other places of your runtime?std::list
instead of more conventional containers to store shared pointers?If you answer YES to all the bullet points, then to achieve your goal you'll probably have to design a new class that would behave as a holder for your
shared_ptr<list<shared_ptr<typeB>>>
, while only providingconst
access to the elements.If, however, on one of the bullet points your answer is NO, consider redesigning the
l
type. I suggest starting withstd::vector<typeB>
and then only adding necessary modifications one by one.