B
Bob Hairgrove
The following class contains start and end points of a range of
values. The range can have a direction which is implied from the
relative order of start and end, or it can be without direction. IOW
if start is greater than end, and direction == true, then the range
has reverse direction. If direction == false, the starting point is
always less than the end point.
To make getting the intersection of two ranges easier, for example, I
also want to keep a "normalized" start and end for which start < end
even if the real start is greater than the end. Since the template
parameter E (for element type) can be anything, I don't want to
generate possibly expensive copies of the start and end elements if
they are user-defined types. Therefore, I decided to try storing this
information in pointers to members.
Most of the time this works OK. But for ranges with negative
direction, although the elem_ptr members are apparently pointing to
the correct members (i.e. reversed), when I dereference them I get the
wrong data: e.g. this->*m_pNormalizedStart will return m_start instead
of m_end, although the debugger is showing "&Range<int,int>::m_end"
for m_pNormalizedStart.
I know I can just use a member function to return the normalized data
and only need to compare m_start to m_end. That wouldn't be a problem,
also since it could be inlined, but I want to know in general if using
pointers to members like this is OK.
Is there something "fishy" about how I am setting the member pointers
in the constructor? I know that the object isn't fully constructed yet
at member initialization time; however, the member data is initialized
first and therefore should have a valid address.
I also thought that the compiler might be treating the pointers to
memebers like static data (since that is the syntax for setting their
value) and overwriting them when a new Range is created with a
different direction. However, I assume that each Range object would
have a distinct set of pointers to member. Maybe this isn't a valid
assumption?
I hope I am just making some other stupid mistake, but I have looked
hard and not yet found anything. I don't think it is a compiler bug
because I get the same behavior when running my test code compiled on
GCC and Borland.
Here is some skeleton code:
// declarations:
template<typename E, typename D = E>
class Range
{
typedef E Range::*elem_ptr;
public:
Range<E,D>::Range()
: m_start()
, m_end()
, m_directed(false)
, m_pNormalizedStart(&Range::m_start)
, m_pNormalizedEnd(&Range::m_end){;}
Range<E,D>::Range(const E & startElem
, const E & endElem
, bool directed)
: m_start( directed ?
startElem : (startElem > endElem ? endElem : startElem))
, m_end ( directed ?
endElem : (startElem < endElem ? endElem : startElem))
, m_directed(directed)
, m_pNormalizedStart(m_start <= m_end ?
&Range::m_start : &Range::m_end)
, m_pNormalizedEnd(m_start <= m_end ?
&Range::m_end : &Range::m_start){;}
// etc.
private:
E m_start;
E m_end;
bool m_directed;
elem_ptr m_pNormalizedStart;
elem_ptr m_pNormalizedEnd;
};
TIA
values. The range can have a direction which is implied from the
relative order of start and end, or it can be without direction. IOW
if start is greater than end, and direction == true, then the range
has reverse direction. If direction == false, the starting point is
always less than the end point.
To make getting the intersection of two ranges easier, for example, I
also want to keep a "normalized" start and end for which start < end
even if the real start is greater than the end. Since the template
parameter E (for element type) can be anything, I don't want to
generate possibly expensive copies of the start and end elements if
they are user-defined types. Therefore, I decided to try storing this
information in pointers to members.
Most of the time this works OK. But for ranges with negative
direction, although the elem_ptr members are apparently pointing to
the correct members (i.e. reversed), when I dereference them I get the
wrong data: e.g. this->*m_pNormalizedStart will return m_start instead
of m_end, although the debugger is showing "&Range<int,int>::m_end"
for m_pNormalizedStart.
I know I can just use a member function to return the normalized data
and only need to compare m_start to m_end. That wouldn't be a problem,
also since it could be inlined, but I want to know in general if using
pointers to members like this is OK.
Is there something "fishy" about how I am setting the member pointers
in the constructor? I know that the object isn't fully constructed yet
at member initialization time; however, the member data is initialized
first and therefore should have a valid address.
I also thought that the compiler might be treating the pointers to
memebers like static data (since that is the syntax for setting their
value) and overwriting them when a new Range is created with a
different direction. However, I assume that each Range object would
have a distinct set of pointers to member. Maybe this isn't a valid
assumption?
I hope I am just making some other stupid mistake, but I have looked
hard and not yet found anything. I don't think it is a compiler bug
because I get the same behavior when running my test code compiled on
GCC and Borland.
Here is some skeleton code:
// declarations:
template<typename E, typename D = E>
class Range
{
typedef E Range::*elem_ptr;
public:
Range<E,D>::Range()
: m_start()
, m_end()
, m_directed(false)
, m_pNormalizedStart(&Range::m_start)
, m_pNormalizedEnd(&Range::m_end){;}
Range<E,D>::Range(const E & startElem
, const E & endElem
, bool directed)
: m_start( directed ?
startElem : (startElem > endElem ? endElem : startElem))
, m_end ( directed ?
endElem : (startElem < endElem ? endElem : startElem))
, m_directed(directed)
, m_pNormalizedStart(m_start <= m_end ?
&Range::m_start : &Range::m_end)
, m_pNormalizedEnd(m_start <= m_end ?
&Range::m_end : &Range::m_start){;}
// etc.
private:
E m_start;
E m_end;
bool m_directed;
elem_ptr m_pNormalizedStart;
elem_ptr m_pNormalizedEnd;
};
TIA