//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // // template // constexpr R invoke_r(F&& f, Args&&... args) // C++23 // noexcept(is_nothrow_invocable_r_v); #include #include #include #include #include // declval template concept can_invoke_r = requires { { std::invoke_r(std::declval(), std::declval()...) } -> std::same_as; }; constexpr bool test() { // Make sure basic functionality works (i.e. we actually call the function and // return the right result). { auto f = [](int i) { return i + 3; }; assert(std::invoke_r(f, 4) == 7); } // Make sure invoke_r is SFINAE-friendly { auto f = [](int) -> char* { return nullptr; }; static_assert( can_invoke_r); static_assert( can_invoke_r); static_assert( can_invoke_r); // discard return type static_assert(!can_invoke_r); // wrong argument type static_assert(!can_invoke_r); // missing argument static_assert(!can_invoke_r); // incompatible return type static_assert(!can_invoke_r); // discard return type, invalid argument type } // Make sure invoke_r has the right noexcept specification { auto f = [](int) noexcept(true) -> char* { return nullptr; }; auto g = [](int) noexcept(false) -> char* { return nullptr; }; struct ConversionNotNoexcept { constexpr ConversionNotNoexcept(char*) noexcept(false) { } }; static_assert( noexcept(std::invoke_r(f, 0))); static_assert(!noexcept(std::invoke_r(g, 0))); // function call is not noexcept static_assert(!noexcept(std::invoke_r(f, 0))); // function call is noexcept, conversion isn't static_assert(!noexcept(std::invoke_r(g, 0))); // function call and conversion are both not noexcept } // Make sure invoke_r works with cv-qualified void return type { auto check = [] { bool was_called = false; auto f = [&](int) -> char* { was_called = true; return nullptr; }; std::invoke_r(f, 3); assert(was_called); static_assert(std::is_void_v(f, 3))>); }; check.template operator()(); check.template operator()(); // volatile void is deprecated, so not testing it // const volatile void is deprecated, so not testing it } // Make sure invoke_r forwards its arguments { struct NonCopyable { NonCopyable() = default; NonCopyable(NonCopyable const&) = delete; NonCopyable(NonCopyable&&) = default; }; // Forward argument, with void return { bool was_called = false; auto f = [&](NonCopyable) { was_called = true; }; std::invoke_r(f, NonCopyable()); assert(was_called); } // Forward argument, with non-void return { bool was_called = false; auto f = [&](NonCopyable) -> int { was_called = true; return 0; }; (void)std::invoke_r(f, NonCopyable()); assert(was_called); } // Forward function object, with void return { struct MoveOnlyVoidFunction { bool& was_called; constexpr void operator()() && { was_called = true; } }; bool was_called = false; std::invoke_r(MoveOnlyVoidFunction{was_called}); assert(was_called); } // Forward function object, with non-void return { struct MoveOnlyIntFunction { bool& was_called; constexpr int operator()() && { was_called = true; return 0; } }; bool was_called = false; (void)std::invoke_r(MoveOnlyIntFunction{was_called}); assert(was_called); } } // Make sure invoke_r performs an implicit conversion of the result { struct Convertible { constexpr operator int() const { return 42; } }; auto f = []() -> Convertible { return Convertible{}; }; int result = std::invoke_r(f); assert(result == 42); } // Note: We don't test that `std::invoke_r` works with all kinds of callable types here, // since that is extensively tested in the `std::invoke` tests. return true; } int main(int, char**) { test(); static_assert(test()); return 0; }