Commit e022c806 by Aaron Jacobs Committed by Copybara-Service

overload: allow using the underlying type with CTAD directly.

There's no need to go through an extra layer of indirection to get this.

PiperOrigin-RevId: 627567108
Change-Id: I241851c5294417aae069047e35961ed58475e59e
parent 564372fc
...@@ -16,33 +16,26 @@ ...@@ -16,33 +16,26 @@
// File: overload.h // File: overload.h
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// `absl::Overload()` returns a functor that provides overloads based on the // `absl::Overload` is a functor that provides overloads based on the functors
// functors passed to it. // with which it is created. This can, for example, be used to locally define an
// anonymous visitor type for `std::visit` inside a function using lambdas.
//
// Before using this function, consider whether named function overloads would // Before using this function, consider whether named function overloads would
// be a better design. // be a better design.
// One use case for this is locally defining visitors for `std::visit` inside a
// function using lambdas.
// Example: Using `absl::Overload` to define a visitor for `std::variant`.
// //
// std::variant<int, std::string, double> v(int{1}); // Note: absl::Overload requires C++17.
//
// assert(std::visit(absl::Overload(
// [](int) -> absl::string_view { return "int"; },
// [](const std::string&) -> absl::string_view {
// return "string";
// },
// [](double) -> absl::string_view { return "double"; }),
// v) == "int");
// //
// One of the lambda may specify overload for several types via generic lambda. // Example:
// //
// absl::variant<std::string, int32_t, int64_t> v(int32_t{1}); // std::variant<std::string, int32_t, int64_t> v(int32_t{1});
// assert(std::visit(absl::Overload( // const size_t result =
// std::visit(absl::Overload{
// [](const std::string& s) { return s.size(); }, // [](const std::string& s) { return s.size(); },
// [](const auto& s) { return sizeof(s); }), v) == 4); // [](const auto& s) { return sizeof(s); },
// },
// v);
// assert(result == 4);
// //
// Note: absl::Overload requires C++17.
#ifndef ABSL_FUNCTIONAL_OVERLOAD_H_ #ifndef ABSL_FUNCTIONAL_OVERLOAD_H_
#define ABSL_FUNCTIONAL_OVERLOAD_H_ #define ABSL_FUNCTIONAL_OVERLOAD_H_
...@@ -56,14 +49,30 @@ ABSL_NAMESPACE_BEGIN ...@@ -56,14 +49,30 @@ ABSL_NAMESPACE_BEGIN
#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ #if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
template <int&... ExplicitArgumentBarrier, typename... T> template <typename... T>
auto Overload(T&&... ts) { struct Overload final : T... {
struct OverloadImpl : absl::remove_cvref_t<T>... { using T::operator()...;
using absl::remove_cvref_t<T>::operator()...;
}; // For historical reasons we want to support use that looks like a function
return OverloadImpl{std::forward<T>(ts)...}; // call:
} //
// absl::Overload(lambda_1, lambda_2)
//
// This works automatically in C++20 because we have support for parenthesized
// aggregate initialization. Before then we must provide a constructor that
// makes this work.
//
explicit Overload(T... ts) : T(std::move(ts))... {}
};
// Before C++20, which added support for CTAD for aggregate types, we must also
// teach the compiler how to deduce the template arguments for Overload.
//
template <typename... T>
Overload(T...) -> Overload<T...>;
#else #else
namespace functional_internal { namespace functional_internal {
template <typename T> template <typename T>
constexpr bool kDependentFalse = false; constexpr bool kDependentFalse = false;
...@@ -76,6 +85,7 @@ auto Overload(T&&...) { ...@@ -76,6 +85,7 @@ auto Overload(T&&...) {
} }
#endif #endif
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
...@@ -31,14 +31,15 @@ ...@@ -31,14 +31,15 @@
namespace { namespace {
TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) {
auto overloaded = absl::Overload( auto overloaded = absl::Overload{
[](int v) -> std::string { return absl::StrCat("int ", v); }, // [](int v) -> std::string { return absl::StrCat("int ", v); },
[](double v) -> std::string { return absl::StrCat("double ", v); }, // [](double v) -> std::string { return absl::StrCat("double ", v); },
[](const char* v) -> std::string { // [](const char* v) -> std::string {
return absl::StrCat("const char* ", v); // return absl::StrCat("const char* ", v);
}, // },
[](auto v) -> std::string { return absl::StrCat("auto ", v); } // [](auto v) -> std::string { return absl::StrCat("auto ", v); },
); };
EXPECT_EQ("int 1", overloaded(1)); EXPECT_EQ("int 1", overloaded(1));
EXPECT_EQ("double 2.5", overloaded(2.5)); EXPECT_EQ("double 2.5", overloaded(2.5));
EXPECT_EQ("const char* hello", overloaded("hello")); EXPECT_EQ("const char* hello", overloaded("hello"));
...@@ -46,32 +47,34 @@ TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { ...@@ -46,32 +47,34 @@ TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) {
} }
TEST(OverloadTest, DispatchConsidersNumberOfArguments) { TEST(OverloadTest, DispatchConsidersNumberOfArguments) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a) { return a + 1; }, // [](int a) { return a + 1; },
[](int a, int b) { return a * b; }, // [](int a, int b) { return a * b; },
[]() -> absl::string_view { return "none"; } // []() -> absl::string_view { return "none"; },
); };
EXPECT_EQ(3, overloaded(2)); EXPECT_EQ(3, overloaded(2));
EXPECT_EQ(21, overloaded(3, 7)); EXPECT_EQ(21, overloaded(3, 7));
EXPECT_EQ("none", overloaded()); EXPECT_EQ("none", overloaded());
} }
TEST(OverloadTest, SupportsConstantEvaluation) { TEST(OverloadTest, SupportsConstantEvaluation) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a) { return a + 1; }, // [](int a) { return a + 1; },
[](int a, int b) { return a * b; }, // [](int a, int b) { return a * b; },
[]() -> absl::string_view { return "none"; } // []() -> absl::string_view { return "none"; },
); };
static_assert(overloaded() == "none"); static_assert(overloaded() == "none");
static_assert(overloaded(2) == 3); static_assert(overloaded(2) == 3);
static_assert(overloaded(3, 7) == 21); static_assert(overloaded(3, 7) == 21);
} }
TEST(OverloadTest, PropogatesDefaults) { TEST(OverloadTest, PropogatesDefaults) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a, int b = 5) { return a * b; }, // [](int a, int b = 5) { return a * b; },
[](double c) { return c; } // [](double c) { return c; },
); };
EXPECT_EQ(21, overloaded(3, 7)); EXPECT_EQ(21, overloaded(3, 7));
EXPECT_EQ(35, overloaded(7)); EXPECT_EQ(35, overloaded(7));
...@@ -79,53 +82,59 @@ TEST(OverloadTest, PropogatesDefaults) { ...@@ -79,53 +82,59 @@ TEST(OverloadTest, PropogatesDefaults) {
} }
TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) { TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a, int b = 5) { return a * b; }, // [](int a, int b = 5) { return a * b; },
[](int c) { return c; } // [](int c) { return c; },
); };
static_assert(!std::is_invocable_v<decltype(overloaded), int>); static_assert(!std::is_invocable_v<decltype(overloaded), int>);
static_assert(std::is_invocable_v<decltype(overloaded), int, int>); static_assert(std::is_invocable_v<decltype(overloaded), int, int>);
} }
TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) { TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a) { return a; }, // [](int a) { return a; },
[](int c) { return c; } // [](int c) { return c; },
); };
static_assert(!std::is_invocable_v<decltype(overloaded), int>); static_assert(!std::is_invocable_v<decltype(overloaded), int>);
} }
TEST(OverloadTest, AmbiguousConversionNotInvocable) { TEST(OverloadTest, AmbiguousConversionNotInvocable) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](uint16_t a) { return a; }, // [](uint16_t a) { return a; },
[](uint64_t c) { return c; } // [](uint64_t c) { return c; },
); };
static_assert(!std::is_invocable_v<decltype(overloaded), int>); static_assert(!std::is_invocable_v<decltype(overloaded), int>);
} }
TEST(OverloadTest, AmbiguousConversionWithAutoNotInvocable) { TEST(OverloadTest, AmbiguousConversionWithAutoNotInvocable) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](auto a) { return a; }, // [](auto a) { return a; },
[](auto c) { return c; } // [](auto c) { return c; },
); };
static_assert(!std::is_invocable_v<decltype(overloaded), int>); static_assert(!std::is_invocable_v<decltype(overloaded), int>);
} }
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
TEST(OverloadTest, AmbiguousConversionWithAutoAndTemplateNotInvocable) { TEST(OverloadTest, AmbiguousConversionWithAutoAndTemplateNotInvocable) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](auto a) { return a; }, // [](auto a) { return a; },
[]<class T>(T c) { return c; } // []<class T>(T c) { return c; },
); };
static_assert(!std::is_invocable_v<decltype(overloaded), int>); static_assert(!std::is_invocable_v<decltype(overloaded), int>);
} }
TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) { TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](int a) { return a; }, // [](int a) { return a; },
[]<class T>(T c) { return c * 2; } // []<class T>(T c) { return c * 2; },
); };
EXPECT_EQ(7, overloaded(7)); EXPECT_EQ(7, overloaded(7));
EXPECT_EQ(14.0, overloaded(7.0)); EXPECT_EQ(14.0, overloaded(7.0));
} }
...@@ -133,20 +142,22 @@ TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) { ...@@ -133,20 +142,22 @@ TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) {
#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L #endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
TEST(OverloadTest, DispatchConsidersSfinae) { TEST(OverloadTest, DispatchConsidersSfinae) {
auto overloaded = absl::Overload( // auto overloaded = absl::Overload{
[](auto a) -> decltype(a + 1) { return a + 1; } // [](auto a) -> decltype(a + 1) { return a + 1; },
); };
static_assert(std::is_invocable_v<decltype(overloaded), int>); static_assert(std::is_invocable_v<decltype(overloaded), int>);
static_assert(!std::is_invocable_v<decltype(overloaded), std::string>); static_assert(!std::is_invocable_v<decltype(overloaded), std::string>);
} }
TEST(OverloadTest, VariantVisitDispatchesCorrectly) { TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
absl::variant<int, double, std::string> v(1); absl::variant<int, double, std::string> v(1);
auto overloaded = absl::Overload( auto overloaded = absl::Overload{
[](int) -> absl::string_view { return "int"; }, // [](int) -> absl::string_view { return "int"; },
[](double) -> absl::string_view { return "double"; }, // [](double) -> absl::string_view { return "double"; },
[](const std::string&) -> absl::string_view { return "string"; } // [](const std::string&) -> absl::string_view { return "string"; },
); };
EXPECT_EQ("int", absl::visit(overloaded, v)); EXPECT_EQ("int", absl::visit(overloaded, v));
v = 1.1; v = 1.1;
EXPECT_EQ("double", absl::visit(overloaded, v)); EXPECT_EQ("double", absl::visit(overloaded, v));
...@@ -156,10 +167,11 @@ TEST(OverloadTest, VariantVisitDispatchesCorrectly) { ...@@ -156,10 +167,11 @@ TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) { TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) {
absl::variant<std::string, int32_t, int64_t> v(int32_t{1}); absl::variant<std::string, int32_t, int64_t> v(int32_t{1});
auto overloaded = absl::Overload( auto overloaded = absl::Overload{
[](const std::string& s) { return s.size(); }, // [](const std::string& s) { return s.size(); },
[](const auto& s) { return sizeof(s); } // [](const auto& s) { return sizeof(s); },
); };
EXPECT_EQ(4, absl::visit(overloaded, v)); EXPECT_EQ(4, absl::visit(overloaded, v));
v = int64_t{1}; v = int64_t{1};
EXPECT_EQ(8, absl::visit(overloaded, v)); EXPECT_EQ(8, absl::visit(overloaded, v));
...@@ -167,6 +179,23 @@ TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) { ...@@ -167,6 +179,23 @@ TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) {
EXPECT_EQ(5, absl::visit(overloaded, v)); EXPECT_EQ(5, absl::visit(overloaded, v));
} }
// This API used to be exported as a function, so it should also work fine to
// use parantheses when initializing it.
TEST(OverloadTest, UseWithParentheses) {
const auto overloaded =
absl::Overload([](const std::string& s) { return s.size(); },
[](const auto& s) { return sizeof(s); });
absl::variant<std::string, int32_t, int64_t> v(int32_t{1});
EXPECT_EQ(4, absl::visit(overloaded, v));
v = int64_t{1};
EXPECT_EQ(8, absl::visit(overloaded, v));
v = std::string("hello");
EXPECT_EQ(5, absl::visit(overloaded, v));
}
} // namespace } // namespace
#endif #endif
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