29
loading...
This website collects cookies to deliver better user experience
const
have to do with smart pointers?const
reference. We are going to see that it's a bad idea.const
both with the pointer itself or with the pointed value.std::unique_ptr
, I'll use that one throughout our examples, except when I explicitly need a shared pointer to demonstrate a concept.const
and not what we point to. It means that we cannot reset the pointer, we cannot change what it points to. At the same time, the value it points to can be modified.#include <memory>
int main() {
const std::unique_ptr<int> p = std::make_unique<int>(42);
++(*p); // OK, data is not const
// p.reset(new int{666}); // ERROR cannot reset const pointer
}
const unique_ptr
, you're very limited in what you can do. You cannot even return it as it requires moving away from it.const std::unique_ptr<int> f() {
const std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
/*
error: use of deleted function
'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&)
[with _Tp = int; _Dp = std::default_delete<int>]'
| return p;
| ^
*/
const
is not a problem starting from C++17. The below snippet produces the same error on C++14 as the above one, but passes with C++17:const std::unique_ptr<int> f() {
std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
const
, but the pointer itself is mutable. In other words, you cannot change the value of the pointed data, but you can change what the pointer points to.#include <memory>
int main() {
std::unique_ptr<const int> p = std::make_unique<const int>(42);
// ++(*p); // ERROR, data is const
p.reset(new int{51}); // OK, pointer is not const
}
std::make_unique<const int>(42)
the const
is not mandatory, the code will compile even if we forget the const
. But we should rather not forget it. If you check it in compiler explorer, missing the const
results in an extra move constructror call and we also have to destruct the temporary object:mov DWORD PTR [rbp-20], 42
lea rax, [rbp-32]
lea rdx, [rbp-20]
mov rsi, rdx
mov rdi, rax
call std::_MakeUniq<int>::__single_object std::make_unique<int, int>(int&&)
lea rdx, [rbp-32]
lea rax, [rbp-40]
mov rsi, rdx
mov rdi, rax
call std::unique_ptr<int const, std::default_delete<int const> >::unique_ptr<int, std::default_delete<int>, void>(std::unique_ptr<int, std::default_delete<int> >&&)
lea rax, [rbp-32]
mov rdi, rax
call std::unique_ptr<int, std::default_delete<int> >::~unique_ptr() [complete object destructor]
const
within std::make_unique
, the above code simplifies to:mov DWORD PTR [rbp-36], 42
lea rax, [rbp-48]
lea rdx, [rbp-36]
mov rsi, rdx
mov rdi, rax
call std::_MakeUniq<int const>::__single_object std::make_unique<int const, int>(int&&)
const
smart pointer, use const
both on the left and the right side given that you use the std::make_*
functions.const
s. In this case, both the pointed value and the (smart) pointer are const, therefore no change is accepted.#include <memory>
int main() {
const std::unique_ptr<const int> p = std::make_unique<const int>(42);
// ++(*p); // ERROR, data is const
// p.reset(new int{51}); // ERROR, pointer is const
}
const
on the right-hand side!delete
manually.const
) reference, what you don't pass around is the (shared) ownership. In other words, you don't deal with ownership at all. If you don't deal with ownership, there is no need for smart pointers.std::weak_ptr
there is no problem in general. It's not so widely used, one could even say it's a niche and those who need it and use it, know exactly how to do that.unique_ptr
is often used incorrectly. It's just passed around by const
reference because many don't know how to use it and how to read C++'s lengthy error messages.unique_ptr
and returns it, there is no problem.#include <memory>
std::unique_ptr<int> foo() {
std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
int main() {
auto p = foo();
}
unique_ptr
as an argument and later returns it, problems start to arise:#include <memory>
std::unique_ptr<int> bar() {
std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
void foo(std::unique_ptr<int> ip) {
++(*ip);
}
int main() {
auto p = bar();
foo(p);
}
/*
main.cpp: In function 'int main()':
main.cpp:14:6: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
14 | foo(p);
| ~~~^~~
*/
uniqe_ptr
to a function and it doesn't work as you have a nasty error message.foo(std::unique_ptr<int>)
and it would work. Unfortunately, it's not the right thing to do, but it's an easy try and it works. main.cpp: In function 'int main()':
main.cpp:14:6: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
14 | foo(p);
| ~~~^~~
unique_ptr
is not copyable, after all, it's supposed to be unique! So there is no copy constructor. But it doesn't mean we should pass it by (const
) reference, no. It means we should move it, so the right solution is this:#include <memory>
std::unique_ptr<int> bar() {
std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
void foo(std::unique_ptr<int> ip) {
++(*ip);
}
int main() {
auto p = bar();
foo(std::move(p));
}
#include <memory>
std::unique_ptr<int> bar() {
std::unique_ptr<int> p = std::make_unique<int>(42);
return p;
}
void foo(int* ip) {
++(*ip);
}
int main() {
auto p = bar();
foo(p.get());
}
shared_ptr
by (const
) reference. There are no syntactical difficulties like with its unique counterpart. shared_pointer
.const
. First, we saw what it means to have a const
smart pointer or a smart pointer to a const
value.const
reference instead of value and we should never do that, we should always pass a smart pointer by value.29