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.
Thanks for the comments on the question. Summarizing them as an asnwer.
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