Prevent inefficient copying when using not_null::get (#1059)

Closes issue #550, which highlighted overhead in not_null::get for larger types such as shared_ptr. Every call to get would return a copy of the contained value.
This PR implements Herb's suggestion for changing the return type of not_null::get. The not_null's value will now only be copied if it is "trivially copyable"; otherwise, it will be returned by const reference.
Note: this change also forces the returned copy to be const.
This commit is contained in:
dmitrykobets-msft 2022-10-11 16:49:16 -07:00 committed by GitHub
parent 7d49d4b45d
commit 991fa6682e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 13 additions and 2 deletions

View File

@ -46,6 +46,17 @@ namespace details
: std::true_type : std::true_type
{ {
}; };
// Resolves to the more efficient of `const T` or `const T&`, in the context of returning a const-qualified value
// of type T.
//
// Copied from cppfront's implementation of the CppCoreGuidelines F.16 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-in)
template<typename T>
using value_or_reference_return_t = std::conditional_t<
sizeof(T) < 2*sizeof(void*) && std::is_trivially_copy_constructible<T>::value,
const T,
const T&>;
} // namespace details } // namespace details
// //
@ -104,7 +115,7 @@ public:
not_null(const not_null& other) = default; not_null(const not_null& other) = default;
not_null& operator=(const not_null& other) = default; not_null& operator=(const not_null& other) = default;
constexpr std::conditional_t<std::is_copy_constructible<T>::value, T, const T&> get() const constexpr details::value_or_reference_return_t<T> get() const
{ {
Ensures(ptr_ != nullptr); Ensures(ptr_ != nullptr);
return ptr_; return ptr_;

View File

@ -52,7 +52,7 @@ template <typename T>
struct CustomPtr struct CustomPtr
{ {
CustomPtr(T* p) : p_(p) {} CustomPtr(T* p) : p_(p) {}
operator T*() { return p_; } operator T*() const { return p_; }
bool operator!=(std::nullptr_t) const { return p_ != nullptr; } bool operator!=(std::nullptr_t) const { return p_ != nullptr; }
T* p_ = nullptr; T* p_ = nullptr;
}; };