string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable DeclarationsOriginally posted as TotW #188 on December 10, 2020
Updated 2020-12-10
Quicklink: abseil.io/tips/188
What is wrong with this code?
bool CanYouPetTheDog(const std::shared_ptr<Dog>& dog, absl::Duration min_delay) { return dog->GetLastPetTime() + min_delay < absl::Now(); }
The function CanYouPetTheDog
does not affect the ownership of its dog
argument, yet its signature demands that it should be stored in a
std::shared_ptr
. This creates an unnecessary dependency on a specific
ownership model, even though nothing in the function requires it. This
dependency prevents callers from using other models, such as std::unique_ptr
or constructing objects on the stack.
By using a reference, we can remove the dependency on a specific ownership
model, and allow our function to work with any object of type Dog
.
bool CanYouPetTheDog(const Dog& dog, absl::Duration min_delay) { return dog.GetLastPetTime() + min_delay < absl::Now(); }
With the above definition, the function can be called regardless of the caller’s ownership model:
Dog stack_dog; if (CanYouPetTheDog(stack_dog, delay)) { ... } auto heap_dog = std::make_unique<Dog>(); if (CanYouPetTheDog(*heap_dog, delay)) { ... } CustomPetPtr<Dog> custom_dog = CreateDog(); if (CanYouPetTheDog(*custom_dog, delay)) { ... }
If the function modifies the passed value, pass a mutable reference or a raw pointer, and use the same idioms as shown above.
The following code provides several overloads for different smart pointer
parameters. The first overload assumes ownership of the passed object and the
second one adds a shared reference to the passed object. Both of these
operations depend on how the caller handles ownership of the Dog
. Adopting a
Dog
that lives on the stack isn’t possible, as ownership can’t be taken away
from the stack.
class Human { public: ... // Transfers ownership of `dog` to this Human. // See Tip #117 for the rationale for accepting std::unique_ptr by value. void Adopt(std::unique_ptr<Dog> dog) { pets_.push_back(std::move(dog)); } // Adds a shared reference to `cat`. void Adopt(std::shared_ptr<Cat> cat) { pets_.push_back(std::move(cat)); } private: std::vector<std::shared_ptr<Pet>> pets_; ... };
If ownership is not being transferred or modified, avoid having smart pointers as function parameters.