Search code examples
c++allocator

How to conditionally select base constructor in initialization list of derived constructor


Example: A wrapper for std::vector. I have 2 move constructors:

template <class Allocator>
class MyVector {
  ....
  MyVector(MyVector&&) = default;
  MyVector(MyVector&& other, const Allocator<int>& alloc) : vec(std::move(other.vec), alloc) {}
private:
      std::vector<int, Allocator<int>> vec;

  ...
}

However, I want to do an optimization to avoid a costly constructor of vector in case that the given memory allocator is the same as in the moved parameter. Something like:

class MyVector {
  MyVector(MyVector&& other, const Allocator<int>& alloc)
      : if (other.vec.get_allocator() == alloc) 
            vec(std::move(other.vec)) 
        else 
            vec(std::move(other.vec), alloc) 
        {}
}

Is this even possible in C++?

Note: Question Right way to conditionally initialize a C++ member variable? is not similar, as I cannot push the condition inside the base constructor function call. I need it outside to choose the base constructor.


Context: A third party library code, which I can't change, uses the wrong move constructor (passing allocator when it should not be passed), which I am trying to fix, because it extremely harms the performance.

More context: The problematic code is std::scoped_allocator_adaptor. It treats std::pair as a container, which makes that problem.

Having set<pair<int,MyVector>>, and using 1 scoped allocator for all memory allocations, it generates the wrong constructor in allocator_traits::construct(). The moved MyVector indeed uses the same allocator, but it is obscured by the pair, and the fact that vec.get_allocator() == set.get_allocator() is ignored. So pair construction invokes the move constructor of MyVector with the unnecessary alloc parameter.


Solution

  • Thanks for the comments on the question. Summarizing them as an asnwer.

    1. It seems impossible to use condition to select base constructors.
    2. As a solution I removed the scoped allocator adaptor and just changed the code from MyVector v; v.emplace_back() to v.emplace_back(MyVector::value_type{v.get_allocator()}; thus effectively adding the scoped behaviour by myself. That bypassed the problematic slow constructor and I indeed measure a considerable gain in speed. Same changes were done for other containers, like sets
    3. Will not add a sample code as it is specific to my problem and not related to the original question, which was purely about C++ syntax.