
On 7/23/07, Eric Niebler <eric@boost-consulting.com> wrote:
General design question here. This came up on the user's list, and it made me question something I believed to be true. When writing a proxy for, e.g., a std container, I see two options for handling const.
0) Const-ness depends on the const-ness of the object being proxied:
1) Const-ness depends on the proxy object itself:
I suppose it really depends on context and how exactly you wish to deal with the proxy, but I would personally try to keep semantics as close as possible to a reference type. You can't directly create a reference type with cv-qualification, and by convention, metafunctions which add cv-qualification to reference types generally yield the input reference type unmodified (such as in Boost.Type_Traits). You obviously can't mimic the former behavior, but to come as close as possible to reference behavior, I'd make cv-qualification of the proxy type not impact the view of the proxied object, and instead make it as much of an implementation detail as possible. Jumping away for a second, given a reference to a vector as a datamember to some type, when this encapsulating type becomes const-qualified, your reference datamember does not all of a sudden reference the vector as though it were const-qualified. Similarly, if you have a proxy to a vector as a datamember, when the encapsulating type becomes const-qualified, your proxy should not all of a sudden reference the vector as though it were const-qualified. You are still dealing with reference semantics so the top-level qualification should not propogate. Because of this, I'd say 0 is the better approach, or at least the most consistent approach with respect to the rest of the language. Honestly, though, I'm not in love with either approach. As I said, I would prefer 0 over 1, but 0's definition of vector_proxy::const_iterator is still troubling to me. Perhaps for practical reasons, such as use with certain generic algorithms, it may make sense to have vector_proxy::const_iterator be the same type as std::vector<int>::iterator as you have shown, but if you absolutely must have the typedef present, I would personally more intuitively prefer it to be the same type as std::vector<int>::const_iterator, though still only provide the single begin() const with a return type of iterator. It really depends on how the proxy is used and what behavior your algorithms expect from the proxies. While likely not practical for many cases, I would even consider a third option, and that is to not include the nested typedefs at all. After all, there is no such thing as std::vector<int>&::iterator. For the sake of hacking together proxies that work with algorithms which expect STL containers, providing the nested typedefs may be the only option to optimize code reuse, but in those cases, you have to realize that what you would be doing truely is a hack, since a proxy itself is not a valid STL container type and therefore shouldn't be passed to algorithms where one is expected. -- -Matt Calabrese