Re: Named std::pair members
Alberto Ganesh Barbati wrote:
Martin T. ha scritto:
Hi all.
I recently stumbled over a discussion that asserted that code using
std::pair<string, int> instead of, say, struct city_t { string name;
int pcode; }; is hard to read as every time we access the pair we just
work with first and second and the user does not immediately see what
first and second should be.
What do you think of the following solution where std::pair is extended
via a preprocessor macro to contain two arbitrarily named member
references in addition to first and second ... ?
It's quite bad, IMHO. There are several drawbacks with this approach:
1) although the C++ standard says that references might not require
storage, in most implementation reference members increase the size of a
class by the size of a pointer. In you case you have to multiply by two
(that is 8 bytes on a lot of platforms). Frankly 8 bytes per element is
*a lot* for a simple readability improvement.
I agree with you and Maxim that the reference approach isn't that good.
The one using accessor functions seems better.
2) in case none of the two member types is const, pair<> is assignable,
but your type is not, because references forbids the declaration of the
implicit assignment operator (see 12.8/12). In the non-const/non-const
case (and only that case) you should provide an explicit assignment
operator.
OK. Something new for me. Never stumbled over this up to now ...
probably because adding reference members is not exactly a day-to-day
thing :)
3) this use of references can be used to circumvent const-correctness:
NAMED_PAIR_TYPE(named_city_t, name, pcode);
typedef named_city_t<std::string, unsigned int> city_t;
const city_t city("Segrate", 20090);
city.name = "Vimodrone"; // Ops! Modifying a const object!
Ouch!! Didn't know that. With pointers it's kinda more obvious.
<snip>
city_t get_city(std::string const& name)
{
city_collection dummy;
if(dummy.find(name) != dummy.end())
return *dummy.find(name);
Of course, you realize that dummy is always empty with this code...
Just was to test that it compiles.
A better idiom, in my opinion, is the following:
typedef std::map<std::string, int> city_collection;
typedef city_collection::value_type city_t;
// you can wrap these in a macro if you want:
inline const std::string& city(const city_t& p) { return p.first; }
inline int& pcode(city_t& p) { return p.second; }
inline const int& pcode(const city_t& p) { return p.second; }
Hmmm ... Both you and Maxim used the free function approach for access.
Personally I much prefer the member-function approach as that can IMHO
help greatly with auto completion / "Intellisense" features.
And it's basically equivalent, or is it?
Here's a modified version:
[code]
#define NAMED_PAIR_TYPE(TNAME, NAMED_FIRST, NAMED_SECOND) \
template<typename T1, typename T2> \
struct TNAME : public std::pair<T1, T2> \
{ \
TNAME() : pair() {} \
\
TNAME(T1 const& val1, T2 const& val2) \
: pair(val1, val2) \
{ } \
\
template<typename OTHER1, typename OTHER2> \
TNAME(pair<OTHER1, OTHER2> const& r) \
: pair(r) \
{ } \
\
/* first, second aliases:*/ \
inline T1 const& NAMED_FIRST() const {return first;} \
inline T2 const& NAMED_SECOND() const {return second;} \
inline T1 & NAMED_FIRST() {return first;} \
inline T2 & NAMED_SECOND() {return second;} \
} \
/*;*/
[/code]
cheers,
Martin
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]