I have a Container
class that is meant to store a vector of shared pointers. Whenever an item is appended to the Container
, I want it to assume ownership of that item. In other words, when the Container
is deconstructed, all of the elements inside it should also be deconstructed.
template <typename T>
class Container
{
private:
const std::vector<std::shared_ptr<T>> vec_;
public:
void append(std::shared_ptr<T> item)
{
vec_.push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_.size(); i++) { std::cout << vec_[i] << std::endl; }
}
};
int main(int argc, char** argv) {
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
return 0;
}
The problem is, I get the following error on vec_.push_back(std::move(item))
.
No matching member function for call to 'push_back'
I'm not sure why this error is occurring.
Your std::vector
, vec_
is const
. Seems to me that would forcefully remove or disable any method, such as push_back()
, which tries to modify the const vector.
I think it's worth expounding upon this comment from @Carpetfizz in the comments under his question, too:
@GabrielStaples thanks, removing
const
did the trick. I thoughtconst
only protected against reassignment of vector_ but allowed for it to be mutated?
My response:
No,
const
here applies to the contents of the vector. The vector isn't a pointer, but what you're talking about could be done with pointers, by making the pointer itselfconst
instead of the contents of what it points toconst
. Also,const
andmutable
are opposites. One undoes the other. You cannot have both in effect at the same time. By definition, something constant is immutable (unchangeable), and something mutable is non-constant.
And how might one make a pointer const but not the contents of what it points to?
First, consider the original code (with some minor modifications/fixes I did to it):
Run it yourself online here: https://onlinegdb.com/SyMqoeU9L
1) cpp_template_const_vector_of_smart_ptrs_test_BEFORE.cpp:
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
class Container
{
private:
// const std::vector<std::shared_ptr<T>> vec_; // does NOT work
std::vector<std::shared_ptr<T>> vec_; // works!
public:
void append(std::shared_ptr<T> item)
{
vec_.push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_.size(); i++)
{
// Don't forget to dereference the pointer with `*` in order to
// obtain the _contens of the pointer_ (ie: what it points to),
// rather than the pointer (address) itself
std::cout << *vec_[i] << std::endl;
}
}
};
int main(int argc, char** argv)
{
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
c->append(std::make_shared<std::string>("world"));
c->printElements();
return 0;
}
Output:
hello
world
And here's the new code demonstrating how to make a constant pointer to a non-const vector. See my comments here, and study the changes:
Run it yourself online here: https://onlinegdb.com/HyjNx-L5U
2) cpp_template_const_vector_of_smart_ptrs_test_AFTER.cpp
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
class Container
{
private:
// const std::vector<std::shared_ptr<T>> vec_; // does NOT work
// Create an alias to this type just to make the creation below less
// redundant in typing out the long type
using vec_type = std::vector<std::shared_ptr<T>>;
// NON-const object (vector)--so it can be changed
vec_type vec_;
// const pointer to NON-const object--so, vec_p_ can NOT be re-assigned to
// point to a new vector, because it is `const`! But, **what it points to**
// CAN be changed because it is NOT const!
vec_type * const vec_p_ = &vec_;
// This also does NOT work (in place of the line above) because it makes
// the **contents of what you're pointing to const**, which means again
// that the contents of the vector can NOT be modified.
// const vec_type * const vec_p_ = &vec_; // does NOT work
// Here's the compile-time error in gcc when compiling for C++17:
// main.cpp: In instantiation of ‘void Container<T>::append(std::shared_ptr<_Tp>) [with T = std::basic_string<char>]’:
// <span class="error_line" onclick="ide.gotoLine('main.cpp',78)">main.cpp:78:53</span>: required from here
// main.cpp:61:9: error: passing ‘const vec_type {aka const std::vector >, std::allocator > > >}’ as ‘this’ argument discards qualifiers [-fpermissive]
// vec_p_->push_back(std::move(item));
// ^~~~~~
// In file included from /usr/include/c++/7/vector:64:0,
// from main.cpp:22:
// /usr/include/c++/7/bits/stl_vector.h:953:7: note: in call to ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::shared_ptr >; _Alloc = std::allocator > >; std::vector<_Tp, _Alloc>::value_type = std::shared_ptr >]’
// push_back(value_type&& __x)
// ^~~~~~~~~
// To prove that vec_p_ can NOT be re-assigned to point to a new vector,
// watch this:
vec_type vec2_;
// vec_p_ = &vec2_; // COMPILE-TIME ERROR! Here is the error:
// main.cpp:44:5: error: ‘vec_p_’ does not name a type; did you mean ‘vec_type’?
// vec_p_ = &vec2_; // COMPILE-TIME ERROR!
// ^~~~~~
// vec_type
// BUT, this works just fine:
vec_type * vec_p2_ = &vec2_; // non-const pointer to non-const data
public:
void append(std::shared_ptr<T> item)
{
vec_p_->push_back(std::move(item));
}
void printElements()
{
for (int i = 0; i < vec_p_->size(); i++)
{
// Notice we have to use a double de-reference here now!
std::cout << *(*vec_p_)[i] << std::endl;
}
}
};
int main(int argc, char** argv)
{
std::unique_ptr<Container<std::string>> c = std::make_unique<Container<std::string>>();
c->append(std::make_shared<std::string>("hello"));
c->append(std::make_shared<std::string>("world"));
c->printElements();
return 0;
}
Output:
hello
world
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.