Commit 017f0cee authored by François Degros's avatar François Degros Committed by Commit Bot

Unified scoped_refptr's assignment operators.

Replaced several of scoped_refptr's assignment operators by a unified assignment
operator. This works beautifully in tandem with scoped_refptr's different
constructors, and it removes some duplicated and tricky code. It is exception
safe, and works fine in case of self assignment.

We still need to keep operator=(T*), despite the implicit conversion constructor
scoped_refptr(T*). There is code assigning rtc::scoped_refptr<T> to
scoped_refptr<T>, and we want to keep this code compiling and working as is.
rtc::scoped_refptr<T> has an implicit conversion operator to T*. C++ allows one
implicit conversion, but not two, to be chained in a row.

Marked scoped_refptr's move and swap operations as noexcept. This allows
std::vector<scoped_refptr<T>> to use scoped_refptr's more efficient move
operations instead of copy operations. This avoids extraneous calls to
T::AddRef() and T::Release() when vector is reallocating its internal buffer.

Change-Id: Id6f360f8acab827133fe5bef829a7a2e308b87fd
Reviewed-on: https://chromium-review.googlesource.com/802338Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: François Degros <fdegros@chromium.org>
Cr-Commit-Position: refs/heads/master@{#524008}
parent 9faa2ca7
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include <type_traits>
#include <utility> #include <utility>
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
...@@ -161,6 +162,12 @@ TEST(RefCountedUnitTest, TestSelfAssignment) { ...@@ -161,6 +162,12 @@ TEST(RefCountedUnitTest, TestSelfAssignment) {
scoped_refptr<SelfAssign> var(p); scoped_refptr<SelfAssign> var(p);
var = var; var = var;
EXPECT_EQ(var.get(), p); EXPECT_EQ(var.get(), p);
var = std::move(var);
EXPECT_EQ(var.get(), p);
var.swap(var);
EXPECT_EQ(var.get(), p);
swap(var, var);
EXPECT_EQ(var.get(), p);
} }
TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) { TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) {
...@@ -563,21 +570,21 @@ TEST(RefCountedUnitTest, MoveConstructorDerived) { ...@@ -563,21 +570,21 @@ TEST(RefCountedUnitTest, MoveConstructorDerived) {
} }
TEST(RefCountedUnitTest, TestOverloadResolutionCopy) { TEST(RefCountedUnitTest, TestOverloadResolutionCopy) {
scoped_refptr<Derived> derived(new Derived); const scoped_refptr<Derived> derived(new Derived);
scoped_refptr<SelfAssign> expected(derived); const scoped_refptr<SelfAssign> expected(derived);
EXPECT_EQ(expected, Overloaded(derived)); EXPECT_EQ(expected, Overloaded(derived));
scoped_refptr<Other> other(new Other); const scoped_refptr<Other> other(new Other);
EXPECT_EQ(other, Overloaded(other)); EXPECT_EQ(other, Overloaded(other));
} }
TEST(RefCountedUnitTest, TestOverloadResolutionMove) { TEST(RefCountedUnitTest, TestOverloadResolutionMove) {
scoped_refptr<Derived> derived(new Derived); scoped_refptr<Derived> derived(new Derived);
scoped_refptr<SelfAssign> expected(derived); const scoped_refptr<SelfAssign> expected(derived);
EXPECT_EQ(expected, Overloaded(std::move(derived))); EXPECT_EQ(expected, Overloaded(std::move(derived)));
scoped_refptr<Other> other(new Other); scoped_refptr<Other> other(new Other);
scoped_refptr<Other> other2(other); const scoped_refptr<Other> other2(other);
EXPECT_EQ(other2, Overloaded(std::move(other))); EXPECT_EQ(other2, Overloaded(std::move(other)));
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <iosfwd> #include <iosfwd>
#include <type_traits> #include <type_traits>
#include <utility>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/logging.h" #include "base/logging.h"
...@@ -164,7 +165,7 @@ class scoped_refptr { ...@@ -164,7 +165,7 @@ class scoped_refptr {
} }
// Copy constructor. // Copy constructor.
scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) { scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) {
if (ptr_) if (ptr_)
AddRef(ptr_); AddRef(ptr_);
} }
...@@ -180,13 +181,13 @@ class scoped_refptr { ...@@ -180,13 +181,13 @@ class scoped_refptr {
// Move constructor. This is required in addition to the conversion // Move constructor. This is required in addition to the conversion
// constructor below in order for clang to warn about pessimizing moves. // constructor below in order for clang to warn about pessimizing moves.
scoped_refptr(scoped_refptr&& r) : ptr_(r.get()) { r.ptr_ = nullptr; } scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; }
// Move conversion constructor. // Move conversion constructor.
template <typename U, template <typename U,
typename = typename std::enable_if< typename = typename std::enable_if<
std::is_convertible<U*, T*>::value>::type> std::is_convertible<U*, T*>::value>::type>
scoped_refptr(scoped_refptr<U>&& r) : ptr_(r.get()) { scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.ptr_) {
r.ptr_ = nullptr; r.ptr_ = nullptr;
} }
...@@ -212,48 +213,15 @@ class scoped_refptr { ...@@ -212,48 +213,15 @@ class scoped_refptr {
return ptr_; return ptr_;
} }
scoped_refptr<T>& operator=(T* p) { scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
// AddRef first so that self assignment should work
if (p)
AddRef(p);
T* old_ptr = ptr_;
ptr_ = p;
if (old_ptr)
Release(old_ptr);
return *this;
}
scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
return *this = r.ptr_;
}
template <typename U>
scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
return *this = r.get();
}
scoped_refptr<T>& operator=(scoped_refptr<T>&& r) {
scoped_refptr<T> tmp(std::move(r));
tmp.swap(*this);
return *this;
}
template <typename U> // Unified assignment operator.
scoped_refptr<T>& operator=(scoped_refptr<U>&& r) { scoped_refptr& operator=(scoped_refptr r) noexcept {
// We swap with a temporary variable to guarantee that |ptr_| is released swap(r);
// immediately. A naive implementation which swaps |this| and |r| would
// unintentionally extend the lifetime of |ptr_| to at least the lifetime of
// |r|.
scoped_refptr<T> tmp(std::move(r));
tmp.swap(*this);
return *this; return *this;
} }
void swap(scoped_refptr<T>& r) { void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); }
T* tmp = ptr_;
ptr_ = r.ptr_;
r.ptr_ = tmp;
}
explicit operator bool() const { return ptr_ != nullptr; } explicit operator bool() const { return ptr_ != nullptr; }
...@@ -351,7 +319,7 @@ std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) { ...@@ -351,7 +319,7 @@ std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) {
} }
template <typename T> template <typename T>
void swap(scoped_refptr<T>& lhs, scoped_refptr<T>& rhs) { void swap(scoped_refptr<T>& lhs, scoped_refptr<T>& rhs) noexcept {
lhs.swap(rhs); lhs.swap(rhs);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment