Commit 5cae9645 authored by Hidehiko Abe's avatar Hidehiko Abe Committed by Commit Bot

Implement conditional copy/move ctors/assign-operators.

BUG=784732
TEST=Ran trybot.

Change-Id: Iec5f9eaa7482d4e23f5bf2eea4b34c9cd867f89d
Reviewed-on: https://chromium-review.googlesource.com/856021
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532004}
parent 090ff5d8
...@@ -265,6 +265,58 @@ class OptionalBase { ...@@ -265,6 +265,58 @@ class OptionalBase {
OptionalStorage<T> storage_; OptionalStorage<T> storage_;
}; };
// The following {Copy,Move}{Constructible,Assignable} structs are helpers to
// implement constructor/assign-operator overloading. Specifically, if T is
// is not movable but copyable, Optional<T>'s move constructor should not
// participate in overload resolution. This inheritance trick implements that.
template <bool is_copy_constructible>
struct CopyConstructible {};
template <>
struct CopyConstructible<false> {
constexpr CopyConstructible() = default;
constexpr CopyConstructible(const CopyConstructible&) = delete;
constexpr CopyConstructible(CopyConstructible&&) = default;
CopyConstructible& operator=(const CopyConstructible&) = default;
CopyConstructible& operator=(CopyConstructible&&) = default;
};
template <bool is_move_constructible>
struct MoveConstructible {};
template <>
struct MoveConstructible<false> {
constexpr MoveConstructible() = default;
constexpr MoveConstructible(const MoveConstructible&) = default;
constexpr MoveConstructible(MoveConstructible&&) = delete;
MoveConstructible& operator=(const MoveConstructible&) = default;
MoveConstructible& operator=(MoveConstructible&&) = default;
};
template <bool is_copy_assignable>
struct CopyAssignable {};
template <>
struct CopyAssignable<false> {
constexpr CopyAssignable() = default;
constexpr CopyAssignable(const CopyAssignable&) = default;
constexpr CopyAssignable(CopyAssignable&&) = default;
CopyAssignable& operator=(const CopyAssignable&) = delete;
CopyAssignable& operator=(CopyAssignable&&) = default;
};
template <bool is_move_assignable>
struct MoveAssignable {};
template <>
struct MoveAssignable<false> {
constexpr MoveAssignable() = default;
constexpr MoveAssignable(const MoveAssignable&) = default;
constexpr MoveAssignable(MoveAssignable&&) = default;
MoveAssignable& operator=(const MoveAssignable&) = default;
MoveAssignable& operator=(MoveAssignable&&) = delete;
};
} // namespace internal } // namespace internal
// base::Optional is a Chromium version of the C++17 optional class: // base::Optional is a Chromium version of the C++17 optional class:
...@@ -279,12 +331,18 @@ class OptionalBase { ...@@ -279,12 +331,18 @@ class OptionalBase {
// - No exceptions are thrown, because they are banned from Chromium. // - No exceptions are thrown, because they are banned from Chromium.
// - All the non-members are in the 'base' namespace instead of 'std'. // - All the non-members are in the 'base' namespace instead of 'std'.
template <typename T> template <typename T>
class Optional : public internal::OptionalBase<T> { class Optional
: public internal::OptionalBase<T>,
public internal::CopyConstructible<std::is_copy_constructible<T>::value>,
public internal::MoveConstructible<std::is_move_constructible<T>::value>,
public internal::CopyAssignable<std::is_copy_constructible<T>::value &&
std::is_copy_assignable<T>::value>,
public internal::MoveAssignable<std::is_move_constructible<T>::value &&
std::is_move_assignable<T>::value> {
public: public:
using value_type = T; using value_type = T;
// Defer default/copy/move constructor implementation to OptionalBase. // Defer default/copy/move constructor implementation to OptionalBase.
// TODO(hidehiko): Implement conditional enabling.
constexpr Optional() = default; constexpr Optional() = default;
constexpr Optional(const Optional& other) = default; constexpr Optional(const Optional& other) = default;
constexpr Optional(Optional&& other) = default; constexpr Optional(Optional&& other) = default;
...@@ -315,7 +373,6 @@ class Optional : public internal::OptionalBase<T> { ...@@ -315,7 +373,6 @@ class Optional : public internal::OptionalBase<T> {
~Optional() = default; ~Optional() = default;
// Defer copy-/move- assign operator implementation to OptionalBase. // Defer copy-/move- assign operator implementation to OptionalBase.
// TOOD(hidehiko): Implement conditional enabling.
Optional& operator=(const Optional& other) = default; Optional& operator=(const Optional& other) = default;
Optional& operator=(Optional&& other) = default; Optional& operator=(Optional&& other) = default;
......
...@@ -115,11 +115,29 @@ class DeletedDefaultConstructor { ...@@ -115,11 +115,29 @@ class DeletedDefaultConstructor {
int foo_; int foo_;
}; };
class DeletedCopyConstructor { class DeletedCopy {
public: public:
explicit DeletedCopyConstructor(int foo) : foo_(foo) {} explicit DeletedCopy(int foo) : foo_(foo) {}
DeletedCopyConstructor(const DeletedCopyConstructor&) = delete; DeletedCopy(const DeletedCopy&) = delete;
DeletedCopyConstructor(DeletedCopyConstructor&&) = default; DeletedCopy(DeletedCopy&&) = default;
DeletedCopy& operator=(const DeletedCopy&) = delete;
DeletedCopy& operator=(DeletedCopy&&) = default;
int foo() const { return foo_; }
private:
int foo_;
};
class DeletedMove {
public:
explicit DeletedMove(int foo) : foo_(foo) {}
DeletedMove(const DeletedMove&) = default;
DeletedMove(DeletedMove&&) = delete;
DeletedMove& operator=(const DeletedMove&) = default;
DeletedMove& operator=(DeletedMove&&) = delete;
int foo() const { return foo_; } int foo() const { return foo_; }
...@@ -279,8 +297,18 @@ TEST(OptionalTest, MoveConstructor) { ...@@ -279,8 +297,18 @@ TEST(OptionalTest, MoveConstructor) {
// Even if copy constructor is deleted, move constructor needs to work. // Even if copy constructor is deleted, move constructor needs to work.
// Note that it couldn't be constexpr. // Note that it couldn't be constexpr.
{ {
Optional<DeletedCopyConstructor> first(in_place, 42); Optional<DeletedCopy> first(in_place, 42);
Optional<DeletedCopyConstructor> second(std::move(first)); Optional<DeletedCopy> second(std::move(first));
EXPECT_TRUE(second.has_value());
EXPECT_EQ(42, second->foo());
EXPECT_TRUE(first.has_value());
}
{
Optional<DeletedMove> first(in_place, 42);
Optional<DeletedMove> second(std::move(first));
EXPECT_TRUE(second.has_value()); EXPECT_TRUE(second.has_value());
EXPECT_EQ(42, second->foo()); EXPECT_EQ(42, second->foo());
...@@ -465,6 +493,26 @@ TEST(OptionalTest, AssignObject) { ...@@ -465,6 +493,26 @@ TEST(OptionalTest, AssignObject) {
EXPECT_TRUE(a.value() == TestObject(3, 0.1)); EXPECT_TRUE(a.value() == TestObject(3, 0.1));
EXPECT_TRUE(a == b); EXPECT_TRUE(a == b);
} }
{
Optional<DeletedMove> a(in_place, 42);
Optional<DeletedMove> b;
b = a;
EXPECT_TRUE(!!a);
EXPECT_TRUE(!!b);
EXPECT_EQ(a->foo(), b->foo());
}
{
Optional<DeletedMove> a(in_place, 42);
Optional<DeletedMove> b(in_place, 1);
b = a;
EXPECT_TRUE(!!a);
EXPECT_TRUE(!!b);
EXPECT_EQ(a->foo(), b->foo());
}
} }
TEST(OptionalTest, AssignObject_rvalue) { TEST(OptionalTest, AssignObject_rvalue) {
...@@ -513,6 +561,26 @@ TEST(OptionalTest, AssignObject_rvalue) { ...@@ -513,6 +561,26 @@ TEST(OptionalTest, AssignObject_rvalue) {
EXPECT_EQ(TestObject::State::MOVE_ASSIGNED, a->state()); EXPECT_EQ(TestObject::State::MOVE_ASSIGNED, a->state());
EXPECT_EQ(TestObject::State::MOVED_FROM, b->state()); EXPECT_EQ(TestObject::State::MOVED_FROM, b->state());
} }
{
Optional<DeletedMove> a(in_place, 42);
Optional<DeletedMove> b;
b = std::move(a);
EXPECT_TRUE(!!a);
EXPECT_TRUE(!!b);
EXPECT_EQ(42, b->foo());
}
{
Optional<DeletedMove> a(in_place, 42);
Optional<DeletedMove> b(in_place, 1);
b = std::move(a);
EXPECT_TRUE(!!a);
EXPECT_TRUE(!!b);
EXPECT_EQ(42, b->foo());
}
} }
TEST(OptionalTest, AssignNull) { TEST(OptionalTest, AssignNull) {
......
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