Eric Fiselier a9e659619f Implement N4606 optional
Summary:
Adapt implementation of Library Fundamentals TS optional into an implementation of N4606 optional.

  - Update relational operators per http://wg21.link/P0307
  - Update to requirements of http://wg21.link/P0032
  - Extension: Implement trivial copy/move construction/assignment for `optional<T>` when `T` is trivially copyable.

Audit P/Rs for optional LWG issues:
  - 2756 "C++ WP optional<T> should 'forward' T's implicit conversions" Implemented, which also resolves 2753 "Optional's constructors and assignments need constraints" (modulo my refusal to explicitly delete the move operations, which is a design error that I'm working on correcting in the 2756 P/R).
  - 2736 "nullopt_t insufficiently constrained" Already conforming. I've added a test ensuring that `nullopt_t` is not copy-initializable from an empty braced-init-list, which I believe is the root intent of the issue, to avoid regression.
  - 2740 "constexpr optional<T>::operator->" Already conforming.
  - 2746 "Inconsistency between requirements for emplace between optional and variant" No P/R, but note that the author's '"suggested resolution" is already implemented.
  - 2748 "swappable traits for optionals" Already conforming.
  - 2753 "Optional's constructors and assignments need constraints" Implemented.

Most of the work for this patch was done by Casey Carter @ Microsoft. Thank you Casey!



Reviewers: mclow.lists, CaseyCarter, EricWF

Differential Revision: https://reviews.llvm.org/D22741

llvm-svn: 283980
2016-10-12 07:46:20 +00:00

269 lines
7.3 KiB
C++

//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++98, c++03, c++11, c++14
// <optional>
// From LWG2451:
// template <class U>
// optional<T>& operator=(optional<U>&& rhs);
#include <optional>
#include <type_traits>
#include <memory>
#include <cassert>
#include "test_macros.h"
#include "archetypes.hpp"
using std::optional;
struct X
{
static bool throw_now;
X() = default;
X(int &&)
{
if (throw_now)
TEST_THROW(6);
}
};
bool X::throw_now = false;
struct Y1
{
Y1() = default;
Y1(const int&) {}
Y1& operator=(const Y1&) = delete;
};
struct Y2
{
Y2() = default;
Y2(const int&) = delete;
Y2& operator=(const int&) { return *this; }
};
class B {};
class D : public B {};
template <class T>
struct AssignableFrom {
static int type_constructed;
static int type_assigned;
static int int_constructed;
static int int_assigned;
static void reset() {
type_constructed = int_constructed = 0;
type_assigned = int_assigned = 0;
}
AssignableFrom() = default;
explicit AssignableFrom(T) { ++type_constructed; }
AssignableFrom& operator=(T) { ++type_assigned; return *this; }
AssignableFrom(int) { ++int_constructed; }
AssignableFrom& operator=(int) { ++int_assigned; return *this; }
private:
AssignableFrom(AssignableFrom const&) = delete;
AssignableFrom& operator=(AssignableFrom const&) = delete;
};
template <class T> int AssignableFrom<T>::type_constructed = 0;
template <class T> int AssignableFrom<T>::type_assigned = 0;
template <class T> int AssignableFrom<T>::int_constructed = 0;
template <class T> int AssignableFrom<T>::int_assigned = 0;
void test_with_test_type() {
using T = TestTypes::TestType;
T::reset();
{ // non-empty to empty
T::reset_constructors();
optional<T> opt;
optional<int> other(42);
opt = std::move(other);
assert(T::alive == 1);
assert(T::constructed == 1);
assert(T::value_constructed == 1);
assert(T::assigned == 0);
assert(T::destroyed == 0);
assert(static_cast<bool>(other) == true);
assert(*other == 42);
assert(static_cast<bool>(opt) == true);
assert(*opt == T(42));
}
assert(T::alive == 0);
{ // non-empty to non-empty
optional<T> opt(101);
optional<int> other(42);
T::reset_constructors();
opt = std::move(other);
assert(T::alive == 1);
assert(T::constructed == 0);
assert(T::assigned == 1);
assert(T::value_assigned == 1);
assert(T::destroyed == 0);
assert(static_cast<bool>(other) == true);
assert(*other == 42);
assert(static_cast<bool>(opt) == true);
assert(*opt == T(42));
}
assert(T::alive == 0);
{ // empty to non-empty
optional<T> opt(101);
optional<int> other;
T::reset_constructors();
opt = std::move(other);
assert(T::alive == 0);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 1);
assert(static_cast<bool>(other) == false);
assert(static_cast<bool>(opt) == false);
}
assert(T::alive == 0);
{ // empty to empty
optional<T> opt;
optional<int> other;
T::reset_constructors();
opt = std::move(other);
assert(T::alive == 0);
assert(T::constructed == 0);
assert(T::assigned == 0);
assert(T::destroyed == 0);
assert(static_cast<bool>(other) == false);
assert(static_cast<bool>(opt) == false);
}
assert(T::alive == 0);
}
void test_ambigious_assign() {
using OptInt = std::optional<int>;
{
using T = AssignableFrom<OptInt&&>;
T::reset();
{
OptInt a(42);
std::optional<T> t;
t = std::move(a);
assert(T::type_constructed == 1);
assert(T::type_assigned == 0);
assert(T::int_constructed == 0);
assert(T::int_assigned == 0);
}
{
using Opt = std::optional<T>;
static_assert(!std::is_assignable<Opt&, const OptInt&&>::value, "");
static_assert(!std::is_assignable<Opt&, const OptInt&>::value, "");
static_assert(!std::is_assignable<Opt&, OptInt&>::value, "");
}
}
{
using T = AssignableFrom<OptInt const&&>;
T::reset();
{
const OptInt a(42);
std::optional<T> t;
t = std::move(a);
assert(T::type_constructed == 1);
assert(T::type_assigned == 0);
assert(T::int_constructed == 0);
assert(T::int_assigned == 0);
}
T::reset();
{
OptInt a(42);
std::optional<T> t;
t = std::move(a);
assert(T::type_constructed == 1);
assert(T::type_assigned == 0);
assert(T::int_constructed == 0);
assert(T::int_assigned == 0);
}
{
using Opt = std::optional<T>;
static_assert(std::is_assignable<Opt&, OptInt&&>::value, "");
static_assert(!std::is_assignable<Opt&, const OptInt&>::value, "");
static_assert(!std::is_assignable<Opt&, OptInt&>::value, "");
}
}
}
int main()
{
test_with_test_type();
test_ambigious_assign();
{
optional<int> opt;
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt;
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
{
optional<int> opt(3);
optional<short> opt2;
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == false);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
}
{
optional<int> opt(3);
optional<short> opt2(short{2});
opt = std::move(opt2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(static_cast<bool>(opt) == static_cast<bool>(opt2));
assert(*opt == *opt2);
}
{
optional<std::unique_ptr<B>> opt;
optional<std::unique_ptr<D>> other(new D());
opt = std::move(other);
assert(static_cast<bool>(opt) == true);
assert(static_cast<bool>(other) == true);
assert(opt->get() != nullptr);
assert(other->get() == nullptr);
}
#ifndef TEST_HAS_NO_EXCEPTIONS
{
optional<X> opt;
optional<int> opt2(42);
assert(static_cast<bool>(opt2) == true);
try
{
X::throw_now = true;
opt = std::move(opt2);
assert(false);
}
catch (int i)
{
assert(i == 6);
assert(static_cast<bool>(opt) == false);
}
}
#endif
}