Re: Index types based on size_t - safe?
On 30 Sep., 22: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.
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 */
};
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
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]