Commit d39417a5 authored by Hidehiko Abe's avatar Hidehiko Abe Committed by Commit Bot

Implement converting constructors from Optional<U>.

BUG=784732
TEST=Ran trybot.

Change-Id: Icbb9c5596b045ca0c684cdad0343a2605d00dbb1
Reviewed-on: https://chromium-review.googlesource.com/856296Reviewed-by: default avatardanakj <danakj@chromium.org>
Commit-Queue: Hidehiko Abe <hidehiko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533185}
parent 41cfa934
......@@ -2578,6 +2578,7 @@ if (enable_nocompile_tests) {
"memory/weak_ptr_unittest.nc",
"metrics/field_trial_params_unittest.nc",
"metrics/histogram_unittest.nc",
"optional_unittest.nc",
"strings/string16_unittest.nc",
"task_scheduler/task_traits_unittest.nc",
]
......
......@@ -30,6 +30,10 @@ constexpr in_place_t in_place = {};
// http://en.cppreference.com/w/cpp/utility/optional/nullopt
constexpr nullopt_t nullopt(0);
// Forward declaration, which is refered by following helpers.
template <typename T>
class Optional;
namespace internal {
template <typename T, bool = std::is_trivially_destructible<T>::value>
......@@ -219,6 +223,19 @@ class OptionalBase {
constexpr explicit OptionalBase(in_place_t, Args&&... args)
: storage_(in_place, std::forward<Args>(args)...) {}
// Implementation of converting constructors.
template <typename U>
explicit OptionalBase(const OptionalBase<U>& other) {
if (other.storage_.is_populated_)
storage_.Init(other.storage_.value_);
}
template <typename U>
explicit OptionalBase(OptionalBase<U>&& other) {
if (other.storage_.is_populated_)
storage_.Init(std::move(other.storage_.value_));
}
~OptionalBase() = default;
OptionalBase& operator=(const OptionalBase& other) {
......@@ -262,6 +279,11 @@ class OptionalBase {
storage_.is_populated_ = false;
}
// For implementing conversion, allow access to other typed OptionalBase
// class.
template <typename U>
friend class OptionalBase;
OptionalStorage<T> storage_;
};
......@@ -317,6 +339,20 @@ struct MoveAssignable<false> {
MoveAssignable& operator=(MoveAssignable&&) = delete;
};
// Helper to conditionally enable converting constructors.
template <typename T, typename U>
struct IsConvertibleFromOptional
: std::integral_constant<
bool,
std::is_constructible<T, Optional<U>&>::value ||
std::is_constructible<T, const Optional<U>&>::value ||
std::is_constructible<T, Optional<U>&&>::value ||
std::is_constructible<T, const Optional<U>&&>::value ||
std::is_convertible<Optional<U>&, T>::value ||
std::is_convertible<const Optional<U>&, T>::value ||
std::is_convertible<Optional<U>&&, T>::value ||
std::is_convertible<const Optional<U>&&, T>::value> {};
} // namespace internal
// base::Optional is a Chromium version of the C++17 optional class:
......@@ -347,7 +383,47 @@ class Optional
constexpr Optional(const Optional& other) = default;
constexpr Optional(Optional&& other) = default;
constexpr Optional(nullopt_t) {}
constexpr Optional(nullopt_t) {} // NOLINT(runtime/explicit)
// Converting copy constructor. "explicit" only if
// std::is_convertible<const U&, T>::value is false. It is implemented by
// declaring two almost same constructors, but that condition in enable_if_t
// is different, so that either one is chosen, thanks to SFINAE.
template <
typename U,
std::enable_if_t<std::is_constructible<T, const U&>::value &&
!internal::IsConvertibleFromOptional<T, U>::value &&
std::is_convertible<const U&, T>::value,
bool> = false>
Optional(const Optional<U>& other) : internal::OptionalBase<T>(other) {}
template <
typename U,
std::enable_if_t<std::is_constructible<T, const U&>::value &&
!internal::IsConvertibleFromOptional<T, U>::value &&
!std::is_convertible<const U&, T>::value,
bool> = false>
explicit Optional(const Optional<U>& other)
: internal::OptionalBase<T>(other) {}
// Converting move constructor. Similar to converting copy constructor,
// declaring two (explicit and non-explicit) constructors.
template <
typename U,
std::enable_if_t<std::is_constructible<T, U&&>::value &&
!internal::IsConvertibleFromOptional<T, U>::value &&
std::is_convertible<U&&, T>::value,
bool> = false>
Optional(Optional<U>&& other) : internal::OptionalBase<T>(std::move(other)) {}
template <
typename U,
std::enable_if_t<std::is_constructible<T, U&&>::value &&
!internal::IsConvertibleFromOptional<T, U>::value &&
!std::is_convertible<U&&, T>::value,
bool> = false>
explicit Optional(Optional<U>&& other)
: internal::OptionalBase<T>(std::move(other)) {}
constexpr Optional(const T& value)
: internal::OptionalBase<T>(in_place, value) {}
......
......@@ -364,6 +364,65 @@ TEST(OptionalTest, MoveValueConstructor) {
}
}
TEST(OptionalTest, ConvertingCopyConstructor) {
{
Optional<int> first(1);
Optional<double> second(first);
EXPECT_TRUE(second.has_value());
EXPECT_EQ(1.0, second.value());
}
// Make sure explicit is not marked for convertible case.
{
Optional<int> o(1);
ignore_result<Optional<double>>(o);
}
}
TEST(OptionalTest, ConvertingMoveConstructor) {
{
Optional<int> first(1);
Optional<double> second(std::move(first));
EXPECT_TRUE(second.has_value());
EXPECT_EQ(1.0, second.value());
}
// Make sure explicit is not marked for convertible case.
{
Optional<int> o(1);
ignore_result<Optional<double>>(std::move(o));
}
{
class Test1 {
public:
explicit Test1(int foo) : foo_(foo) {}
int foo() const { return foo_; }
private:
int foo_;
};
// Not copyable but convertible from Test1.
class Test2 {
public:
Test2(const Test2&) = delete;
explicit Test2(Test1&& other) : bar_(other.foo()) {}
double bar() const { return bar_; }
private:
double bar_;
};
Optional<Test1> first(in_place, 42);
Optional<Test2> second(std::move(first));
EXPECT_TRUE(second.has_value());
EXPECT_EQ(42.0, second->bar());
}
}
TEST(OptionalTest, ConstructorForwardArguments) {
{
constexpr Optional<float> a(base::in_place, 0.1f);
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a "No Compile Test" suite.
// http://dev.chromium.org/developers/testing/no-compile-tests
#include <type_traits>
#include "base/optional.h"
namespace base {
#if defined(NCTEST_EXPLICIT_CONVERTING_COPY_CONSTRUCTOR) // [r"fatal error: no matching function for call to object of type"]
// Optional<T>(const Optional<U>& arg) constructor is marked explicit if
// T is not convertible from "const U&".
void WontCompile() {
struct Test {
// Declares as explicit so that Test is still constructible from int,
// but not convertible.
explicit Test(int a) {}
};
static_assert(!std::is_convertible<const int&, Test>::value,
"const int& to Test is convertible");
const Optional<int> arg(in_place, 1);
([](Optional<Test> param) {})(arg);
}
#elif defined(NCTEST_EXPLICIT_CONVERTING_MOVE_CONSTRUCTOR) // [r"fatal error: no matching function for call to object of type"]
// Optional<T>(Optional<U>&& arg) constructor is marked explicit if
// T is not convertible from "U&&".
void WontCompile() {
struct Test {
// Declares as explicit so that Test is still constructible from int,
// but not convertible.
explicit Test(int a) {}
};
static_assert(!std::is_convertible<int&&, Test>::value,
"int&& to Test is convertible");
([](Optional<Test> param) {})(Optional<int>(in_place, 1));
}
#endif
} // namespace base
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