I’m struggling with unique pointers in c++
This is the code I’m working with.
std::vector<SomeResult> GetInferenceResults(
const std::vector<std::unique_ptr<FeatureSet>>& feature_sets) const {
std::unordered_map<std::string, std::vector<std::unique_ptr<FeatureSet>>>
feature_sets_by_criterion =
SliceFeatureSetsByCriterion(feature_sets);
std::vector<SomeResult> results =
SomeFunction(feature_sets_by_criterion);
return results;
}
std::unordered_map<std::string, std::vector<std::unique_ptr<FeatureSet>>>
SliceFeatureSetsByCriterion(
const std::vector<std::unique_ptr<FeatureSet>>& feature_sets) {
std::unordered_map<std::string, std::vector<std::unique_ptr<FeatureSet>>>
feature_sets_by_criterion;
std::unordered_map<std::string, int> feature_set_count_by_criterion;
for (const auto& feature_set : feature_sets) {
if (some_condition) {
++feature_set_count_by_criterion["criterion_1"];
} else {
++feature_set_count_by_criterion["criterion_2"];
}
}
for (const auto& [criterion, feature_set_size] :
feature_set_count_by_criterion) {
feature_sets_by_criterion[criterion].reserve(feature_set_size);
}
for (const auto& feature_set : feature_sets) {
feature_sets_by_criterion[GetCriterion(feature_set)].push_back(
std::move(feature_set)); // Error occurring here!
}
return feature_sets_by_criterion;
}
I have two functions as shown above.
The first function, GetInferenceResults, takes in a vector of unique_ptr as an input.
Inside this function, the second function, SliceFeatureSetsByCriterion, is called with the aforementioned input of the first function.
Inside the second function, specifically, in the last for loop (where I added a comment), when the push_back function is executed to add the unique_ptr I get the following error:
In module 'stl:stl_cc_library': crosstool/v18/stable/toolchain/bin/../include/c++/v1/__memory/allocator.h:167:28: **error: call to implicitly-deleted copy constructor of 'std::unique_ptr<FeatureSet>'** 167 | ::new ((void*)__p) _Up(_VSTD::forward<_Args>(__args)...); | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ......... note: in instantiation of member function **'std::vector<std::unique_ptr<FeatureSet>>::push_back' requested here | feature_sets_by_criterion[GetCriterion(feature_set)].push_back(** crosstool/v18/stable/toolchain/bin/../include/c++/v1/__memory/unique_ptr.h:219:59: note: copy constructor is implicitly deleted because 'unique_ptr<FeatureSet>' has a user-declared move constructor 219 | _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr(unique_ptr&& __u) _NOEXCEPT
I’m confused because I’ve taken care of everything to avoid the copying of unique_ptr objects.
For example, I’ve pre-set the size of the std::vector<unique_ptr<>> to avoid the copying of uniqu_ptr objects when multiple unique_ptr objects are added to the vector.
Also, I’m calling std::move inside push_back() function.
I’m running out of ideas to fix this issue.
I’d would really appreciate any of your help and adivce.
>Solution :
std::move(feature_set)); // Error occurring here!
Well, let’s take a look at what feature_set
is:
const std::vector<std::unique_ptr<FeatureSet>>& feature_sets
Oops. It’s a const
object.
Guess what? You cannot move a const
object, by definition. To paraphrase, loosely, "move" means "move": take something from somewhere, rip it out by its roots, and plant it somewhere else. That’s what a move is, in C++ terms.
A const
object, by definition, cannot be changed. It’s roots will remain planted firmly, and no amount of force will dislodge a const
object from where it is.
An attempt to move a const
object devolves into a copy.
And you cannot copy unique_ptr
s.
And that’s the reason for your compilation errors.
You’ll need to rethink the design of your code, and make sure that no attempt is made to "move" any const
object.
I’m calling std::move inside push_back() function.
Many C++ textbooks describe std::move
in such a way that it results in a frequent misconception that std::move
is some kind of magic wand that, once waved, guarantees that a move takes place. It is not. A move happens automatically when certain criteria are met, and std::move
simply provides one of the required conditions, but it is not the only one and only when all conditions are met does a move take place.