Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Index types based on size_t - safe?

2 views
Skip to first unread message

Rune Allnor

unread,
Sep 30, 2009, 4:45:26 PM9/30/09
to
Hi all.

I have an application where I have two main classes,

class A {...};
class B {...};

The application uses two vectors,

std::vector<A> vA;
std::vector<B> vB;

as well as a vector of references (by semantics, not
data type) to elements in the elements in the two vectors:

struct ref {
size_t aref;
size_t bref;
};

where the references aref and bref refer by index to elements
in vectors vA and vB, respectively.

There are lots of manipulator functions that manipulate the
ref arry, to maintain references to A and B elements that
somehow are related. The typical type signature of these
functions is

void foo(size_t ar,size_t br);

where indexes to both A and B elements are of type size_t.

With corresponding potential of mix-up and mayhem.

The simplest way of 'typifying' the references, is

class Aref_t : public size_t {
/* implement relevant assignment and arithmetic operators */
};

class Bref_t : public size_t {
/* implement relevant assignment and arithmetic operators */
};

The question is if this is enough to have the compiler complain
if the two types Aref_t and Bref_t get mixed up - both inherit from
the same base class. .

Are there other ways of doing this? I don't want to use structs,
since I don't want to use type.value kinds of references.

Rune

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Neil Butterworth

unread,
Sep 30, 2009, 8:26:32 PM9/30/09
to
Rune Allnor wrote:
>
> The simplest way of 'typifying' the references, is
>
> class Aref_t : public size_t {
> /* implement relevant assignment and arithmetic operators */
> };


Well, it my be simple, but it is not valid C++. size_t is an integer
type - you cannot inherit from integers.

Neil Butterworth

Neil Butterworth

unread,
Sep 30, 2009, 8:26:33 PM9/30/09
to
Rune Allnor wrote:
> Are there other ways of doing this? I don't want to use structs,
> since I don't want to use type.value kinds of references.

Further to my previous answer, the easiest way to "typify" things really
is to wrap them in a struct:

struct ARef_t {
size_t ref;
};

struct BRef_t {
size_t ref;
};

Now you have two distinct types. Of course, accessing the ref member
becomes a bit more of a hassle, which I guess is why you don't want to
do it. You could give them constructors and conversion operators:

struct BRef_t {
size_t ref;
BRef_t( size_t t ) : ref( t ) {}
operator size_t() { return ref; }
};

but that will almost certainly lead to all sorts of implicit conversion
problems.

Frankly, if you can't live with the type.value syntax, I'd just make
them both typedefs, and live with the possibility of getting them confused.

Neil Butterworth

Daniel Krügler

unread,
Sep 30, 2009, 8:24:12 PM9/30/09
to

I don't understand what you are doing here. Since size_t
is an alias for an unsigned integer type, you cannot use
it as a base class. As an interesting idea for a current
C++0x-draft-compatible compiler would be to use a scoped
enum with an explicit enum-base:

enum class Aref_t : size_t {};
enum class Bref_t : size_t {};

Since scoped enums do not participate in implicit conversions,
this might be useful here.

> The question is if this is enough to have the compiler complain
> if the two types Aref_t and Bref_t get mixed up - both inherit from
> the same base class. .
>
> Are there other ways of doing this? I don't want to use structs,
> since I don't want to use type.value kinds of references.

I don't understand your aversion against structs.
IMO they are at least as useful as a scoped enum:

If I correctly understand your use-case, there exists
a single function which internally needs to refer to
the size_t value, everything else are some member
functions of the wrapper class that support your
arithmetic-like model. To prevent code duplication you
should consider to start with a template instead
of derivation, e.g.

#include <vector>
#include <cstddef>

template<typename Tag>
class Ref_t {
std::size_t value;

friend Tag& element(Ref_t r, std::vector<Tag>& v) {
return v[r.value];
}

friend const Tag& element(Ref_t r, const std::vector<Tag>& v)
{
return v[r.value];
}
public:
// C'tors, arithmetic operators, assignment
};

struct A{};
struct B{};

typedef Ref_t<A> ARef_t;
typedef Ref_t<B> BRef_t;

int main() {
std::vector<A> va;
ARef_t ar;
A& a = element(ar, va);
}

Using friend functions as free-function accessors
is just one approach, you could also use member
functions of Ref_t to realize the same thing.

To simplify const access one could consider to add
a celement accessor:

template<typename T>
const T& celement(Ref_t<T> r, const std::vector<T>& v)
{
return element(r, v);
}

You could also play with "inverted" subscript operator
overloads in Ref_t

Tag& operator[](std::vector<Tag>& v) const {
return v[value];
}

const Tag& operator[](const std::vector<Tag>& v) const {
return v[value];
}

but these have the disadvantage that they might be less
easily understandable and it's harder to explicitly select
the overload returning const Tag&, if the actual vector
is non-const.

HTH & Greetings from Bremen,

Daniel Kr�gler

Nick Hounsome

unread,
Oct 1, 2009, 6:56:33 PM10/1/09
to
On 30 Sep, 21:45, Rune Allnor <all...@tele.ntnu.no> wrote:
> Hi all.
>
> I have an application where I have two main classes,
>
> class A {...};
> class B {...};
>
> The application uses two vectors,
>
> std::vector<A> vA;
> std::vector<B> vB;
>
> as well as a vector of references (by semantics, not
> data type) to elements in the elements in the two vectors:
>
> struct ref {
> size_t aref;
> size_t bref;
> };
>
> where the references aref and bref refer by index to elements
> in vectors vA and vB, respectively.
>
> There are lots of manipulator functions that manipulate the
> ref arry, to maintain references to A and B elements that
> somehow are related. The typical type signature of these
> functions is
>
> void foo(size_t ar,size_t br);
>
> where indexes to both A and B elements are of type size_t.
>
> With corresponding potential of mix-up and mayhem.

You're going about it the wrong (C) way.
Once you've got related information grouped together don't split it up
again unnecessarily.
Just deal with the ref struct and add member functions to it rather
than use free functions on its parts.

Another way is to just overload foo

inline void foo(const ref& r) { foo(r.aref,r.bref); }

terminator

unread,
Oct 4, 2009, 1:19:32 PM10/4/09
to

why not to use iterators:

typedef vector<A> Avec;
typedef vector<B> Bvec;
typedef vector< pair <Avec::iterator,Bvec::iterator> > Rvec;
Avec av[5];
Bvec bv[5];
Rvec rv(10);
rv[5]=makepair(av.begin()+3,bv.end()-1);
A a;
B b;
*(rv[1].first)=a;
b=*(rv[2].second);


cheers,
FM.

0 new messages