From 3c127a77f9b9fb201f98da646bbe0ea06cd6d55b Mon Sep 17 00:00:00 2001 From: Aaryaman Sagar Date: Thu, 28 Dec 2017 08:53:05 -0800 Subject: [PATCH] folly::Indestructible interface improvement Summary: As it stands the user cannot use initializer list constructors in the underlying type, this fixes that and provides a good interface change. This allows them to use list initialization, which works with initializer lists Reviewed By: yfeldblum Differential Revision: D6620994 fbshipit-source-id: c29199f97b434d84dd8d4cee2f00c5eccb316166 --- folly/Indestructible.h | 64 +++++++++++++++++++++++++++---- folly/test/IndestructibleTest.cpp | 36 +++++++++++++++++ 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/folly/Indestructible.h b/folly/Indestructible.h index 56ece823..9dbeb7d3 100644 --- a/folly/Indestructible.h +++ b/folly/Indestructible.h @@ -17,8 +17,11 @@ #pragma once #include +#include #include +#include + namespace folly { /*** @@ -61,10 +64,57 @@ class Indestructible final { template constexpr Indestructible() noexcept(noexcept(T())) {} - template ()...))> + /** + * Constructor accepting a single argument by forwarding reference, this + * allows using list initialzation without the overhead of things like + * in_place, etc and also works with std::initializer_list constructors + * which can't be deduced, the default parameter helps there. + * + * auto i = folly::Indestructible>{{{1, 2}}}; + * + * This provides convenience + * + * There are two versions of this constructor - one for when the element is + * implicitly constructible from the given argument and one for when the + * type is explicitly but not implicitly constructible from the given + * argument. + */ + template < + typename U = T, + _t::value>>* = nullptr, + _t, remove_cvref_t>::value>>* = + nullptr, + _t::value>>* = nullptr> + explicit constexpr Indestructible(U&& u) noexcept( + noexcept(T(std::declval()))) + : storage_(std::forward(u)) {} + template < + typename U = T, + _t::value>>* = nullptr, + _t, remove_cvref_t>::value>>* = + nullptr, + _t::value>>* = nullptr> + /* implicit */ constexpr Indestructible(U&& u) noexcept( + noexcept(T(std::declval()))) + : storage_(std::forward(u)) {} + + template ()...))> explicit constexpr Indestructible(Args&&... args) noexcept( - noexcept(T(std::declval()...))) + noexcept(T(std::declval()...))) : storage_(std::forward(args)...) {} + template < + typename U, + typename... Args, + typename = decltype( + T(std::declval&>(), + std::declval()...))> + explicit constexpr Indestructible(std::initializer_list il, Args... args) noexcept( + noexcept( + T(std::declval&>(), + std::declval()...))) + : storage_(il, std::forward(args)...) {} ~Indestructible() = default; @@ -72,12 +122,12 @@ class Indestructible final { Indestructible& operator=(Indestructible const&) = delete; Indestructible(Indestructible&& other) noexcept( - noexcept(T(std::declval()))) + noexcept(T(std::declval()))) : storage_(std::move(other.storage_.value)) { other.erased_ = true; } Indestructible& operator=(Indestructible&& other) noexcept( - noexcept(T(std::declval()))) { + noexcept(T(std::declval()))) { storage_.value = std::move(other.storage_.value); other.erased_ = true; } @@ -106,11 +156,9 @@ class Indestructible final { template constexpr Storage() noexcept(noexcept(T())) : value() {} - template < - typename... Args, - typename = decltype(T(std::declval()...))> + template ()...))> explicit constexpr Storage(Args&&... args) noexcept( - noexcept(T(std::declval()...))) + noexcept(T(std::declval()...))) : value(std::forward(args)...) {} ~Storage() {} diff --git a/folly/test/IndestructibleTest.cpp b/folly/test/IndestructibleTest.cpp index ad57b1fa..a4869837 100644 --- a/folly/test/IndestructibleTest.cpp +++ b/folly/test/IndestructibleTest.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -119,3 +120,38 @@ TEST_F(IndestructibleTest, disabled_default_ctor) { EXPECT_FALSE((std::is_constructible, Magic>::value)); EXPECT_TRUE((std::is_constructible, int>::value)); } + +TEST_F(IndestructibleTest, list_initialization) { + auto map = folly::Indestructible>{{{1, 2}}}; + EXPECT_EQ(map->at(1), 2); +} + +namespace { +class InitializerListConstructible { + public: + InitializerListConstructible(InitializerListConstructible&&) = default; + explicit InitializerListConstructible(std::initializer_list) {} + InitializerListConstructible(std::initializer_list, double) {} +}; +} // namespace + +TEST_F(IndestructibleTest, initializer_list_in_place_initialization) { + using I = InitializerListConstructible; + std::ignore = Indestructible{{1, 2, 3, 4}}; + std::ignore = Indestructible{{1.2}, 4.2}; +} + +namespace { +class ExplicitlyMoveConstructible { + public: + ExplicitlyMoveConstructible() = default; + explicit ExplicitlyMoveConstructible(ExplicitlyMoveConstructible&&) = default; +}; +} // namespace + +TEST_F(IndestructibleTest, list_initialization_explicit_implicit) { + using E = ExplicitlyMoveConstructible; + using I = std::map; + EXPECT_TRUE((!std::is_convertible>::value)); + EXPECT_TRUE((std::is_convertible>::value)); +} -- 2.34.1