10

I'm having trouble finding a good answer to this. For some reason I thought STL sort would be implemented using swap for better support of complicated types, but as I ended up digging through the code a bit it appears it is actually doing a binary copy. Can someone confirm this? I guess binary copy would actually be preferred to swap.

Side Question: Are any of the STL algorithms or container operations implemented using swap? (Outside of std::swap obviously.) I want to be aware of when it is prudent to implement my own swap for complicated types.

Edit: Reason I'm asking is if you have something like:

class MyClass {
  vector<int> vec_data;
  int a;
  int b;
}
vector<MyClass> my_vec;
sort(my_vec.begin(), my_vec.end(), MyCustomCompare);

I want to make sure the sort isn't calling the copy constructor of the vector, which would happen if you called the default Copy constructor of MyData. Hence my question is sort calling swap, copy assign, etc?

Jason T.
  • 362
  • 2
  • 11
  • 2
    You are probably looking at a `sort` specialization for integer (or other native) types. – Praetorian Nov 21 '11 at 22:42
  • It _would_ be clever to specialize `std::sort` for `POD` types. It's not allowed to do binary copy for all types though. – Mooing Duck Nov 21 '11 at 22:47
  • So what is std::sort using? Copy & assign, or std::swap, or something else... – Jason T. Nov 21 '11 at 23:00
  • 1
    Are you really talking about the STL? – PlasmaHH Nov 21 '11 at 23:03
  • @PlasmaHH: I assume he really means the C++ Standard Library. – Mooing Duck Nov 21 '11 at 23:22
  • @PlasmaHH & Mooing Duck - I guess I'm confused at the distinction between STL and C++ Standard Library. sort is part of the stl right: http://www.sgi.com/tech/stl/sort.html – Jason T. Nov 22 '11 at 00:11
  • 2
    @tamulj: If people want to get pedantic, "STL" is the original STL. It was incorporated with minor changes into the C++ standard as part of the standardization process. While the C++ Standard does not refer to that section of the standard library as the STL, that's generally what a C++ programmer means when they say STL. – Billy ONeal Nov 22 '11 at 01:54
  • 1
    @tamulj, you can read about the STL vs C++ Standard Library terminology [here](http://stackoverflow.com/questions/5205491/whats-this-stl-vs-c-standard-library-fight-all-about). – Blastfurnace Nov 22 '11 at 01:54
  • It seems your use of the words "binary copy" for describing an ordinary copy using the copy constructor confuses some of the answerers. I'm pretty sure you don't mean a hard binary copy, but the invocation of the (maybe non-trivial) copy constructor in contrast to an invocation of `std::swap`. – Christian Rau Nov 22 '11 at 15:42

4 Answers4

6

No, std::sort from the C++ Standard Library is not allowed to do a binary copy on objects with non-trivial copy/assignment operators. I don't see why it can't do binary copies on objects with trivial copy/assignment operators though. Consider this object:

class Explosive {
    Explosive* const self;
public:
    Explosive() :self(this) {}
    Explosive(const Explosive&) :self(this) {}
    ~Explosive() {assert(this==self);}
    Explosive& operator=(const Explosive& rhs) {
        assert(this==self && rhs.self==&rhs); 
        return *this;
    }
    bool operator<(const Explosive& rhs) const 
    {return std::less<Explosive*>(self,rhs.self);}
};

The C++ algorithms are guaranteed to not set off either assert, which means a binary copy would not be valid.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • where is it written ("not allowed")? – sehe Nov 21 '11 at 22:53
  • @sehe: I said "not allowed for most types". It is allowed to do that for some. If that's not in the standard somewhere I'll go learn java. – Mooing Duck Nov 21 '11 at 22:59
  • I don't think the reason is that it's *not allowed*; the result would most likely be nonsensical if a binary copy was employed instead of swap. – Praetorian Nov 21 '11 at 23:00
  • @Praetorian: I figured the demo code made it clear why it wasn't allowed, though I didn't say so explicitly. – Mooing Duck Nov 21 '11 at 23:12
  • 1
    @sehe: It's not allowed by 3.8.5: ... the program has undefined behavior if: ... "the pointer is used to access a non-static data member or call a non-static member function of the object, or". Doing a memcpy style copy would require that the implementation convert to e.g. `char *` first, after which they are not allowed to touch the contents of the pointer. – Billy ONeal Nov 21 '11 at 23:14
  • @MooingDuck: which IMO has zero relevance to sorting initialized elements – sehe Nov 21 '11 at 23:21
  • @BillyONeal: the fact is that GNU libstdc++ implements `std::copy` as `memmove` whenever the `has_trivial_assignment_operator` is true – sehe Nov 21 '11 at 23:25
  • @sehe: It's allowed to use `memmove` when the underlying type is a POD type. It's got to be more specific than has_trivial_assignment_operator -- if it has a nontrivial destructor for instance then the compiler still can't use memmove. Oh, and just because libstdc++ does things some way does not mean that is the right way. – Billy ONeal Nov 22 '11 at 01:48
4

It depends on your STL implementation. The GCC STL implementation uses a combination of introsort and insertion sort. It appears that std::swap (or your specialization) will be invoked in the introsort loop, but it will not be invoked by the insertion sort.

If you don't have a specialization of std::swap, then the default std::swap implementation will use a temporary copy to implement swap.

It does not use binary copy (except for some POD types, which may have specializations hidden deep within the STL libraries).

Also, in C++0x, it seems likely that the insertion sort will use move semantics (rvalue references).

Laramie
  • 56
  • 2
  • @ Laramie, don't you mean: If you don't have a specialization of swap, you get the default std::swap. This seems to be the answer to my question. The insertion sort portion of the swap is using the copy constructor and assignment operator to move data around. – Jason T. Nov 22 '11 at 19:31
3

I've seen before that std::copy employs memmove in GNU libstdc++, depending on whether the element type is POD has_trivial_assignment_operator. See the source here:

template<typename _Tp>
   inline _Tp*
   __copy_trivial(const _Tp* __first, const _Tp* __last, _Tp* __result)
   {
      std::memmove(__result, __first, sizeof(_Tp) * (__last - __first));
      return __result + (__last - __first);
   }

At least in SGI, rotate, reverse, swap_ranges, random_shuffle, partition, next_permutation all employ swap.

See http://www.sgi.com/tech/stl/stl_algo.h

Also, the c++11 standard doc for std::sort specificly mentions in § 25.4.1.1:

Requires: RandomAccessIterator shall satisfy the requirements of ValueSwappable (17.6.3.2). The type of *first shall satisfy the requirements of MoveConstructible (Table 20) and of MoveAssignable (Table 22).

Now § 17.6.3.2 contains this:

An object t is swappable with an object u if and only if:

  • the expressions swap(t, u) and swap(u, t) are valid when evaluated in the context described below, and
  • these expressions have the following effects:
    • the object referred to by t has the value originally held by u and
    • the object referred to by u has the value originally held by t.

The context in which swap(t, u) and swap(u, t) are evaluated shall ensure that a binary non-member function named “swap” is selected via overload resolution (13.3) on a candidate set that includes:

  • the two swap function templates defined in (20.2) and
  • the lookup set produced by argument-dependent lookup (3.4.2).
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Not `iter_swap`? I'm suprised. – Billy ONeal Nov 21 '11 at 23:15
  • @BillyONeal [iter_swap is redundant](http://www.sgi.com/tech/stl/iter_swap.html#1), it was defined in terms of swap anyway. I never said I was gonna be exhaustive! I couldn't be because there are many implementations of the standard algorithms – sehe Nov 21 '11 at 23:17
2

It swaps, but since it's a template function it might inline the swapping code. The compiler can choose to do a binary swap as an optimization if it's a simple type.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Technically, the _library_ can choose to do such optimizations. – sehe Nov 21 '11 at 22:49
  • @Sehe: The compiler can as well. – Billy ONeal Nov 21 '11 at 22:53
  • @Mark Ransom - So maybe I'm doing something wrong, but I wrote a totally specialized std template for swap and a non member swap in the same namespace (based on the recommendations of Scott Meyers Effective C++ book) but neither seems to be getting called when I sort(), which is why I started investigating. – Jason T. Nov 21 '11 at 23:21