The issue of const maps and operator[]

  • Thread starter =?ISO-8859-1?Q?Ney_Andr=E9_de_Mello_Zunino?=
  • Start date
?

=?ISO-8859-1?Q?Ney_Andr=E9_de_Mello_Zunino?=

Hello.

Searching around before posting this message revealed that the issue has
been brought up a significant number of times (here are two related
threads [1] [2]). So /std::map<>/ does not provide a const version of
its /operator[]/ member function because that function is supposed to
modify the map in case the passed key is not present. The question which
follows is: why not provide a const overload which would throw when the
given key would not exist? It seems like a natural and easy-to-implement
solution which would contribute to map's associative array role.

In spite of having found several discussions on this topic, I could not
find mention of whether this issue is going to be addressed when C++
gets its next major revision. Could anyone provide some information
(comments, links, etc.) on this?

[1] http://groups.google.com/groups?hl=en&lr=&th=47b92be5c9ed558c&rnum=1
[2] http://groups.google.com/groups?hl=en&lr=&th=698e93dddf8b22d0&rnum=5

Thank you very much,
 
J

James Dennett

Ney said:
Hello.

Searching around before posting this message revealed that the issue has
been brought up a significant number of times (here are two related
threads [1] [2]). So /std::map<>/ does not provide a const version of
its /operator[]/ member function because that function is supposed to
modify the map in case the passed key is not present. The question which
follows is: why not provide a const overload which would throw when the
given key would not exist?

Because it would violate the important principle that adding
const to code should not change its behaviour (except for
propogation of constness to other code), although it may
cause it to no longer compile. The language doesn't enforce
that in isolation; good library design is also necessary.
It seems like a natural and easy-to-implement
solution which would contribute to map's associative array role.

But it's too fragile, for the reason above.
In spite of having found several discussions on this topic, I could not
find mention of whether this issue is going to be addressed when C++
gets its next major revision.

I find it enormously unlikely that this would be changed in the
next revision of C++.

-- James
 
D

Daniel

Ney said:
Hello.

Searching around before posting this message revealed that the issue has
been brought up a significant number of times (here are two related
threads [1] [2]). So /std::map<>/ does not provide a const version of
its /operator[]/ member function because that function is supposed to
modify the map in case the passed key is not present. The question which
follows is: why not provide a const overload which would throw when the
given key would not exist? It seems like a natural and easy-to-implement
solution which would contribute to map's associative array role.

C++ is powerful enough to make this an optional accessory, so it cannot
be used "by accident".

You can write a general wrapper for any map like this:

template <class M>
class const_map
{
const M &imp;

public:
const_map(const M &m) : imp(m) {}

const typename M::value_type::second_type &
operator[](const typename M::key_type &k) const
{
typename M::const_iterator f = imp.find(k);
if (f == imp.end())
throw std::range_error("Not found");

return f->second;
}
};

It can be used as the argument type for a function that only needs to
look up items, and needs throwing semantics for items it can't find.
When a "naked" map is passed to such a function, the compiler
automatically wraps it in a temporary wrapper to impose those
semantics.

// details of a person
typedef std::map<std::string, std::string> person;

// function that wants read-only/throw semantics
void foo(const const_map<person> &p)
{
std::cout << p["name"] << std::endl;
std::cout << p["address"] << std::endl;
std::cout << p["phone"] << std::endl;
}

int main(int argc, char *argv[])
{
try
{
person p;

p["name"] = "Santa";
p["address"] = "North pole";

foo(p);
}
catch (std::exception &e)
{
std::cout << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}

$ g++ cmap.cpp
$ ./a.out
Santa
North pole
Error: Not found
 
T

Thorsten Ottosen

In spite of having found several discussions on this topic, I could not
find mention of whether this issue is going to be addressed when C++
gets its next major revision.

|I find it enormously unlikely that this would be changed in the
|next revision of C++.

yes true. but we are already looking into adding

T& map<K,T>::at( const Key& );
const T& map<K,T>::at( const Key& ) const;

which should throw if nothing is found.
Alternatively, I guess we could use operator()().

-Thorsten
 
J

James Dennett

Thorsten said:
|I find it enormously unlikely that this would be changed in the
|next revision of C++.

yes true. but we are already looking into adding

T& map<K,T>::at( const Key& );
const T& map<K,T>::at( const Key& ) const;

This is reasonable and somewhat consistent with the at
member in other standard library class templates.
which should throw if nothing is found.
Alternatively, I guess we could use operator()().

I wouldn't like that:
mymap[1] = 3;
and
mymap(1) = 3;
would then both be valid, with different semantics in
the case mymap.find(1) == mymap.end(); and identical
semantics otherwise, but they look too similar. Let's
be consistent, and use "at" when we mean range-checked
access which throws if an element is absent.

-- James
 
T

Tom Widmer

|I find it enormously unlikely that this would be changed in the
|next revision of C++.

yes true. but we are already looking into adding

T& map<K,T>::at( const Key& );
const T& map<K,T>::at( const Key& ) const;

which should throw if nothing is found.

I like the symmetry with vector::at.
Alternatively, I guess we could use operator()().

I don't like that, since operator() hasn't been used like that
elsewhere and is usually used for functors.

Tom
 
J

John Nagle

If anything is done to "operator[]", it should be given
the same syntax as "operator()", so we can have classes that
support multiple subscripts. Inconsistently,

foo(a,b)

is a two-argument call, but

foo[a,b]

is an invocation of the comma operator followed by a
one-argument call. One of the more obscure features
of the language.

John Nagle
Animats
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top