简体   繁体   中英

Rule-of-thumb for const semantics for standard library/templated containers?

When someone has written a

template <typename T> class Container;

It's clear enough what happens when you have a const Container<const MyType> (well, assuming you know what const'ness means for MyType ). You can't change anything - no reorder the structure, no adding or removing elements, no changing existing elements; and if something does change under the hood (eg mutable ) you should look the other way because it doesn't count.

It's also clear what you mean by Container<MyType> - everything's mutable (except for const members in mutable classeses etc.)

Things get confusing where you mix constness with non-constness. With const Container<MyType> - is it reasonable to expect to modify the MyType elements? What if the state of the container depends on their values? Also, if I have

using Stuff = Container<MyType>;

I then pass around const Stuff parameters. This opaque wording makes you question whether you're "supposed to" change anything in elements of Stuff; after all, you can't say "deep const on Stuff and don't touch anything in there". And nobody likes writing:

using ConstStuff = Container<const MyType>;

and then passing the awkward const ConstStuff around. And yet, std containers are perfectly (?) capable of living with const ness for themselves but not for their elements.

Even the semantics of Container<const MyType> pose a certain semantic hiccup: You can delete items in the container; and you likely need to be able to make copies of them. That does not make for very const y elements.

Finally, things get even messier when you have multiple templated type parameters. Suppose now it's

template <typename K, typename V> class Container;

(yes, it's like an std::map , which is a motivator for this question.) With this you can have a constant Container but mutable keys - ridiculous, right? You could totally mess it up by mutating them. Or, suppose it's a container with const keys but non- const values, and suppose that it minimizes storage by not keeping multiple copies of the same value, but rather pointing to a single copy of the value. And then you come along and change that value.

Is there some sort of convention or rule-of-thumb about the semantics of const in these cases?

Note: I specifically ignored pointers, and you can ignore them in your answers as well (or not, if you like).

This question would be more suitable for programmers.stackexchange.com, but never mind.

and if something does change under the hood (eg mutable) you should look the other way because it doesn't count.

No, something may legitimately change. Because this const Container may simply be an interface which is specifically unmodifiable by you , while whoever is implementing it may have reserved for themselves the freedom to modify it at will, and this is perfectly fine. Whoever handed you this const Container may have done so in order to not have to instantiate a new container, and to not have to make a safety copy of the contents of their own non-const container.

Things get confusing where you mix constness with non-constness. With const Container - is it reasonable to expect to modify the MyType elements?

Well, yes. That's precisely why it is const Container<MyType> and not const Container<const MyType> . Perfectly reasonable.

What if the state of the container depends on their values?

Okay, that's when things would get confusing. But that would be really weird. (And that's what usually happens with really weird things: they start to get confusing.)

Even the semantics of Container pose a certain semantic hiccup: You can delete items in the container; and you likely need to be able to make copies of them. That does not make for very consty elements.

No, the elements are perfectly consty, because initializing an instance by copying from a const object is precisely what copy constructors do, and because the removal of an object from a container does not imply that any operation needs to be performed on the object, and even if the object was to be destructed as a result of being removed from the container, it is perfectly fine to invoke the destructor of a const object.

Finally, things get even messier when you have multiple templated type parameters. Suppose now it's template <typename K, typename V> class Container; (yes, it's like an std::map, which is a motivator for this question.) With this you can have a constant Container but mutable keys - ridiculous, right?

Yes, that would be kind of ridiculous. But the language has to provide you with the ability to declare a container with immutable keys and mutable values, so the language has to offer the features necessary to accomplish such a thing.

I do not know what to suggest other than to pay close attention to constness.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM