Index: include/llvm/ADT/Optional.h =================================================================== --- include/llvm/ADT/Optional.h +++ include/llvm/ADT/Optional.h @@ -34,44 +34,48 @@ Optional(NoneType) : hasVal(false) {} explicit Optional() : hasVal(false) {} + Optional(const T &y) : hasVal(true) { new (storage.buffer) T(y); } + // This is necessary for disambiguation purposes. + Optional(Optional &O) : hasVal(O.hasVal) { + if (hasVal) + new (storage.buffer) T(*O); + } Optional(const Optional &O) : hasVal(O.hasVal) { if (hasVal) new (storage.buffer) T(*O); } Optional(T &&y) : hasVal(true) { - new (storage.buffer) T(std::forward(y)); + new (storage.buffer) T(std::move(y)); } - Optional(Optional &&O) : hasVal(O) { + Optional(Optional &&O) : hasVal(O) { if (O) { new (storage.buffer) T(std::move(*O)); O.reset(); } } - Optional &operator=(T &&y) { + // This is necessary for disambiguation purposes. + // It behaves like a copy constructor. + Optional(const Optional &&O) : hasVal(O) { if (hasVal) - **this = std::move(y); - else { - new (storage.buffer) T(std::move(y)); - hasVal = true; - } - return *this; - } - Optional &operator=(Optional &&O) { - if (!O) - reset(); - else { - *this = std::move(*O); - O.reset(); - } - return *this; + new (storage.buffer) T(*O); } #if LLVM_HAS_VARIADIC_TEMPLATES + /// Construct an instance containing a value of type \c T constructed with + /// the given arguments. + /// + /// \param Args The arguments with which the \c T object will be + /// direct-initialized. + template + /*implicit*/ Optional(ArgTypes &&...Args) : hasVal(true) { + new (storage.buffer) T(std::forward(Args)...); + } + /// Create a new object by constructing it in place with the given arguments. template void emplace(ArgTypes &&...Args) { @@ -80,32 +84,46 @@ new (storage.buffer) T(std::forward(Args)...); } +#else // LLVM_HAS_VARIADIC_TEMPLATES + + /// Construct an instance containing a value of type \c T from an argument + /// convertible to T, using direct-initialization. + template + /*implicit*/ Optional(U &&Arg) : hasVal(true) { + new (storage.buffer) T(std::forward(Arg)); + } + #endif // LLVM_HAS_VARIADIC_TEMPLATES static inline Optional create(const T* y) { return y ? Optional(*y) : Optional(); } - // FIXME: these assignments (& the equivalent const T&/const Optional& ctors) - // could be made more efficient by passing by value, possibly unifying them - // with the rvalue versions above - but this could place a different set of - // requirements (notably: the existence of a default ctor) when implemented - // in that way. Careful SFINAE to avoid such pitfalls would be required. - Optional &operator=(const T &y) { - if (hasVal) - **this = y; - else { - new (storage.buffer) T(y); + Optional &operator=(const Optional &O) { + if (!O) { + reset(); + } else if (hasVal) { + // Prefer T::operator= when possible. + **this = *O; + } else { + new (storage.buffer) T(*O); hasVal = true; } return *this; } - - Optional &operator=(const Optional &O) { - if (!O) + Optional &operator=(Optional &&O) { + if (!O) { reset(); - else - *this = *O; + } else { + if (hasVal) { + // Prefer T::operator= when possible. + **this = *std::move(O); + } else { + new (storage.buffer) T(*std::move(O)); + hasVal = true; + } + O.reset(); + } return *this; } Index: unittests/ADT/OptionalTest.cpp =================================================================== --- unittests/ADT/OptionalTest.cpp +++ unittests/ADT/OptionalTest.cpp @@ -169,6 +169,62 @@ EXPECT_EQ(0u, NonDefaultConstructible::Destructions); } +TEST_F(OptionalTest, ConstCopyConstruction) { + NonDefaultConstructible::ResetCounts(); + { + const Optional A(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + Optional B(A); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, ConstMoveConstruction) { + NonDefaultConstructible::ResetCounts(); + { + const Optional A(NonDefaultConstructible(3)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + Optional B(std::move(A)); + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(0u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + } + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(2u, NonDefaultConstructible::Destructions); +} + +TEST_F(OptionalTest, ConvertingAssignment) { + NonDefaultConstructible::ResetCounts(); + + Optional A; + A = 5; + EXPECT_TRUE(A.hasValue()); + + EXPECT_EQ(1u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(0u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); + NonDefaultConstructible::ResetCounts(); + + A = 10; + EXPECT_EQ(0u, NonDefaultConstructible::CopyConstructions); + EXPECT_EQ(1u, NonDefaultConstructible::CopyAssignments); + EXPECT_EQ(1u, NonDefaultConstructible::Destructions); +} + TEST_F(OptionalTest, GetValueOr) { Optional A; EXPECT_EQ(42, A.getValueOr(42)); @@ -185,6 +241,18 @@ MultiArgConstructor(int x, int y) : x(x), y(y) {} }; +TEST_F(OptionalTest, VariadicConstruction) { + Optional A(1, 2); + EXPECT_TRUE(A.hasValue()); + EXPECT_EQ(1, A->x); + EXPECT_EQ(2, A->y); + + A = { 3, 4 }; + EXPECT_TRUE(A.hasValue()); + EXPECT_EQ(3, A->x); + EXPECT_EQ(4, A->y); +} + TEST_F(OptionalTest, Emplace) { Optional A; A.emplace(1, 2); @@ -262,9 +330,9 @@ O = MoveOnly(3); EXPECT_TRUE((bool)O); EXPECT_EQ(3, O->val); - EXPECT_EQ(1u, MoveOnly::MoveConstructions); + EXPECT_EQ(2u, MoveOnly::MoveConstructions); EXPECT_EQ(0u, MoveOnly::MoveAssignments); - EXPECT_EQ(1u, MoveOnly::Destructions); + EXPECT_EQ(2u, MoveOnly::Destructions); } TEST_F(OptionalTest, MoveOnlyInitializingAssignment) {