Topic: Swappable base class, unswappable derived class, undefined behaviour?


Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Wed, 27 Sep 2006 18:31:22 CST
Raw View
David Abrahams wrote:
> "Greg Herlihy" <greghe@pacbell.net> writes:
>
> > "Niels Dekker no reply address wrote:
> >> Now suppose a base class is Swappable (according to the Draft), because
> >> it has a swap function.  Does this automatically make a class that is
> >> publicly derived from this base class Swappable as well?
> >
> > The derived class automatically meets the technical requirements of a
> > Swappable type. But it's up to the programmer to decide whether
> > Swappable's semantic requirements have been met as well.
>
> Semantic requirements are a part of the "technical requirements."
> Maybe you meant to say it meets the syntactic, or structural,
> requirements?

Yes, I should have written that the sample program has met Swappable's
"syntactical requirements". In other words, the program has provided a
function with the right name and the right signature - but whether the
function provided also does the right thing when called (that is,
whether it fulfills Swappable's "operational" or "functional"
requirements) by correctly exchanging the values of its two arguments -
that question cannot be answered without an understanding of how the
type of object being swapped, represents its value.

A good example of value data members and non-value data members is a
shared pointer that maintains a raw pointer and int reference count. To
swap two of these shared pointers would necessitate swapping their raw
pointer members - whereas also swapping their reference count data
members would be disasterous. So we can conclude that the raw pointer
data member represents value, while the reference count data member
does not.

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Thu, 28 Sep 2006 16:10:55 GMT
Raw View
"Greg Herlihy" <greghe@pacbell.net> writes:

> A good example of value data members and non-value data members is a
> shared pointer that maintains a raw pointer and int reference count. To
> swap two of these shared pointers would necessitate swapping their raw
> pointer members - whereas also swapping their reference count data
> members would be disasterous.

I'm having a hard time imagining such a smart pointer, frankly.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 29 Sep 2006 12:35:26 CST
Raw View
David Abrahams wrote:
> "Greg Herlihy" <greghe@pacbell.net> writes:
>
> > A good example of value data members and non-value data members is a
> > shared pointer that maintains a raw pointer and int reference count. To
> > swap two of these shared pointers would necessitate swapping their raw
> > pointer members - whereas also swapping their reference count data
> > members would be disasterous.
>
> I'm having a hard time imagining such a smart pointer, frankly.

Consider this code that uses a hypothetical smart_pointer class for an
unidentified type T:

     smart_pointer<T> a1(new T);
     smart_pointer<T> a2 = a1;
     smart_pointer<T> a3 = a1;
     smart_pointer<T> a4 = a1;

     smart_pointer<T> b1(new T);
     smart_pointer<T> b2 = b1;

     swap(a1, b1);

Now say that I want to swap a1 and b1 such that a1 will reference b1's
shared object (and vice versa) and I want all smart_pointers variables
sharing either object (that is, all smart_pointer variables equal to a1
or b1) to have their values be exchanged as well. So the values of a2,
a3, a4 and b2 should all change. Clearly if the program swaps the
reference count along with the shared value, the program will end up
with four instances of a shared object with a reference count of 2, and
two instances of a shared object with a reference count of 4. So the
values must be exchanged and not their reference counts.

Now in order to implement the smart_pointer class and have it even be
able to exchange the values of all four "a" variables with the values
of the two "b" variables as described, smart_pointer's shared type, T,
have to be itself some kind of pointer. In fact T is likely a smart
pointer itself. Because a shared pointer pointer (or a shared shared
pointer) would have the interesting quality described above - in which
updating the value of any one copy updates the value in all of its
copies - in other words, smart_pointer behaves like a smart reference.

A smart reference class would combine the automated memory management
of smart pointers, with with the non-nil guarantee and global value
visibility of references (but not inherit any of references'
syntactical restraints).

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: unknown@this.is.invalid ("Niels Dekker (no reply address)")
Date: Wed, 27 Sep 2006 08:22:12 GMT
Raw View
Thanks to David Abrahams, for clearly answering the questions of my
previous posting, "What if an exception is thrown while swapping
Swappable objects?"

Now suppose a base class is Swappable (according to the Draft), because
it has a swap function.  Does this automatically make a class that is
publicly derived from this base class Swappable as well?

Example:
  #include <algorithm>

  class Base // This class is surely Swappable
  {
    friend void swap(Base& lhs, Base& rhs)
    {
      std::swap(lhs.m_data, rhs.m_data);
    }
  public:
    explicit Base(int arg=0): m_data(arg)
    {
    }
  protected:
    int m_data;
  };

  class Derived: public Base // Is Derived Swappable as well?
  {
  public:
    explicit Derived(int arg1=0, int arg2=0): Base(arg1), m_const(arg2)
    {
    }
  protected:
    const int m_const;
  };


My feeling is that the Derived class is not Swappable.  Because although
the call swap(t, u) is valid for Derived objects t and u, it
certainly does not fulfill the postcondition described in Table 33 of
the Draft (N2009):
  "t has the value originally held by u, and u has the value originally
  held by t"

So it's not allowed to call std functions that impose Swappable
requirements onto their arguments, for this Derived class.  E.g., the
following call to iter_swap is incorrect:

  int main()
  {
    Derived t(22, 44), u(66, 88);

    std::iter_swap(&t, &u);  // Violating the Swappable requirement!?!
  }

Is this little program well defined in C++0x?  Should the compiler issue
an error?  Is the compiler allowed to do so?  Or does it result in
undefined behaviour?

Kind regards,

  Niels Dekker

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Wed, 27 Sep 2006 09:33:23 CST
Raw View
"Niels Dekker no reply address wrote:
> Now suppose a base class is Swappable (according to the Draft), because
> it has a swap function.  Does this automatically make a class that is
> publicly derived from this base class Swappable as well?

The derived class automatically meets the technical requirements of a
Swappable type. But it's up to the programmer to decide whether
Swappable's semantic requirements have been met as well.

> Example:
>   #include <algorithm>
>
>   class Base // This class is surely Swappable
>   {
>     friend void swap(Base& lhs, Base& rhs)
>     {
>       std::swap(lhs.m_data, rhs.m_data);
>     }
>   public:
>     explicit Base(int arg=0): m_data(arg)
>     {
>     }
>   protected:
>     int m_data;
>   };
>
>   class Derived: public Base // Is Derived Swappable as well?
>   {
>   public:
>     explicit Derived(int arg1=0, int arg2=0): Base(arg1), m_const(arg2)
>     {
>     }
>   protected:
>     const int m_const;
>   };

> My feeling is that the Derived class is not Swappable.  Because although
> the call swap(t, u) is valid for Derived objects t and u, it
> certainly does not fulfill the postcondition described in Table 33 of
> the Draft (N2009):
>   "t has the value originally held by u, and u has the value originally
>   held by t"

Since you wrote both the Base and Derived classes, only you know the
answer to that question. To decide whether the Swap routine has swapped
the values of the Derived objects, requires knowing where the value of
a Derived object is to be found in the first place. Specifically, does
Derived m_const's data member participate in the value representation
of the object? Often the answer can be found in the class's
implementation of an equality test. Those class members that constitute
value will be involved in the test.

Basically, there are two possible evaluations:

If Derived's author does not consider its m_const member to be part of
the object's stored value, then swapping two Derived class instances by
swapping only their Base class subobjects would meet all the
requirements of Swappable.

On the other hand, if Derived::m_const (or some other Derived data
member) stores a portion of the object's value - then leaving those
members behind when swapping two Derived objects would clearly not be
exchanging their values, it would be scrambling them. (Ignoring for the
moment that m_const being const could not be swapped)

The Standard allows either answer to be the "right" one. After all,
evaluating a program against what the Standard says can only do so
much. At a certain point, the programmer must decide whether the
program is correct according to the terms that the program has defined
for itself.

Greg

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Wed, 27 Sep 2006 15:26:23 GMT
Raw View
"Greg Herlihy" <greghe@pacbell.net> writes:

> "Niels Dekker no reply address wrote:
>> Now suppose a base class is Swappable (according to the Draft), because
>> it has a swap function.  Does this automatically make a class that is
>> publicly derived from this base class Swappable as well?
>
> The derived class automatically meets the technical requirements of a
> Swappable type. But it's up to the programmer to decide whether
> Swappable's semantic requirements have been met as well.

Semantic requirements are a part of the "technical requirements."
Maybe you meant to say it meets the syntactic, or structural,
requirements?

Everything else you wrote, I agree with.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]