0

The Question

I have a type T that is move assignable and move constructible but not default constructible. I want to create a T temp[N]; using the move constructor so that I can safely move assign Ts into temp.

I am most interested in the case where temp is a class member, but it may be easier to find a solution when temp is a local variable of a function. How can I do that?

Background

I am writing a generic library function that will execute many std::swaps:

template<typename T>
void func(){
    
    while(true){
        // do stuff

        // perform swap
        std::swap(t1, t2);
    }

}

A typical implementation of std::swap is the following:

template<typename T>
void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

Every invocation of std::swap will allocate a T temp and call T temps destructor. I want to eliminate this cost.

If the T is default constructible, I may try something like this:

template<typename T>
void func(){

    T temp;
    
    while(true){
        // do stuff

        // perform swap
        temp = std::move(t1);
        t1 = std::move(t2);
        t2 = std::move(temp);
    }
}

In my case, I cannot assume T is default constructible, but I may assume it is move assignable and move constructible. If I happen to have a t prior to the while loop, I can do the following:

template<typename T>
void func(){
    T temp = std::move(t);
    t = std::move(temp);
    while(true){
        // do stuff

        // perform swap
        t = std::move(t1);
        t1 = std::move(t2);
        t2 = std::move(t);
    }
}

The above code works. However, there is a second optimization I haven't mentioned. After I swap t1 and t2, it is likely that I will soon swap t2 and t3. Instead of performing t2 = std::move(t); followed by temp = std::move(t2);, I want to leave the value of t2 in temp.

At any given moment, there may be at most 8 temp values that have yet to be swapped to their final destination (well, not 8, but a large constexpr number determined by the library user). If T were default constructible, that would look something like this:

template<typename T>
void func(){
    T temp[8];

    while(true){
        // do stuff

        // perform swap
        t1 = std::move(temp[i]);
        temp[i] = std::move(t2);
    }
}

which doesn't work since T temp[8]; calls Ts default constructor. My idea now looks something like this

  1. Allocate some local uninitialized memory for a T temp[8]; with something like malloc.
  2. Move-construct the first 8 temps into the uninitialized memory with placement new.
  3. Somehow ensure destructors are called for the moved-out-of temps.
8
  • If you plan to replace the array with automatic storage duration with dynamic allocation you might as well use std::vector instead Commented Oct 3, 2022 at 12:13
  • Don't call placement-new manually. Use (an array of) std::optional to hold those temporary values. Commented Oct 3, 2022 at 12:15
  • 1
    If we ignore the chained swaps problem, I would say that it's up to T to overload swap() for itself, if the standard implementation is too expensive for it. You'd then do using std::swap; swap(a, b); to swap the object, which will pick up the custom overload, or fall back to std::swap if there's none. Commented Oct 3, 2022 at 12:17
  • @UnholySheep I want automatic storage for sure. I'm pretty sure I can see how it would be done with std::vector. Commented Oct 3, 2022 at 12:19
  • 2
    If you really want to deal with the raw memory yourself then you probably want to have a buffer like alignas(alignof(T)) buffer[sizeof(T) * N]; and for convenience a T* temp = reinterpret_cast<T*>(buffer);. And then you have to deal with the move construction and destruction manually (probably using std::uninitialized_move and std::destroy_at) Commented Oct 3, 2022 at 12:30

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.