Search code examples
c++11move

copy constructor called instead of move constructor, why?


Consider this class:

template<typename T> struct pooled_resource: T{
template<typename... Args> pooled_resource(std::list<T>& pool, Args&&... args):
    T(select_resource(pool, std::forward<Args>(args)...)), pool(pool){
    if(!pool.empty()) pool.pop_front();
}
~pooled_resource(){
    pool.push_front(static_cast<T&&>(*this));
}
private:
std::list<T>& pool;
template<typename... Args> static T select_resource(std::list<T>& pool, Args&&... args){
    if(pool.empty())
        return T(std::forward<Args>(args)...);
    else
        return std::move(pool.front());
}
};

It allows to create variables that have pooled resources but are semantically equivalent to the non-pooled version:

std::list<std::vector<int>> pool;
using pooled_vector = pooled_resource<std::vector<int>>;
{
    pooled_vector a(pool);          //pool is empty, allocate new resources
    { pooled_vector b(pool, 100); }     //allocate again, but then release to pool as b goes out of scope
    pooled_vector c(pool);              //reuse b's resources
    assert(c.size() == 100);
}

My actual problem is that for the simple case above, everything works and the move constructor of the pooled resource is called. However, I'm getting for another class that the move constructor is not called, but rather the copy constructor is. Precisely, the class is boost::compute::vector, that does declare a move constructor which seems to work in simple cases such as boost::compute::vector<int> a; boost::compute::vector<int> b(std::move(a));.

I have no clue why this happens and I don't know how to diagnose it: what am I missing regarding the rules for a move constructor to be actually used?


Solution

  • So the problem was a silly mistake in the class of the pooled resource. This is a sketch of it:

    template<typename T, typename Allocator = DefaultAllocator> struct Resource{
        /*...*/
        Resource(Resource<T>&& o){ /*...*/ }
    }
    

    The problem was that the argument of the move constructor is of type Resource<T, DefaultAllocator>. Since I was using some custom allocator, there was actually no template move constructor available with a generic Allocator, but just with a generic type T and allocator DefaultAllocator. The fix just requires leaving out all template specifications of the move constructor:

    Resource(Resource&& o){ /*...*/ }
    

    Hope this can save somebody's else afternoon.