When unpacking a std::tuple we know that the std::get calls are noexcept, so only the invocation (for std::apply) and construction (for std::make_from_tuple) can throw.
We also know the std::get calls won't throw for a std::array, but this patch doesn't specialize the variable template for std::array. For an arbitrary tuple-like type we don't know if the std::get calls will throw, and so just use a potentially-throwing noexcept-specifier. * include/std/tuple (__unpack_std_tuple): New variable template and partial specializations. (apply, make_from_tuple): Add noexcept-specifier. * testsuite/20_util/tuple/apply/2.cc: New test. * testsuite/20_util/tuple/make_from_tuple/2.cc: New test. Tested x86_64-linux, committed to trunk.
commit 3426bcb0546ef9b4bbc528204da7b79206f8aa87 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Aug 8 21:44:13 2019 +0100 Add noexcept-specifier to std::apply and std::make_from_tuple When unpacking a std::tuple we know that the std::get calls are noexcept, so only the invocation (for std::apply) and construction (for std::make_from_tuple) can throw. We also know the std::get calls won't throw for a std::array, but this patch doesn't specialize the variable template for std::array. For an arbitrary tuple-like type we don't know if the std::get calls will throw, and so just use a potentially-throwing noexcept-specifier. * include/std/tuple (__unpack_std_tuple): New variable template and partial specializations. (apply, make_from_tuple): Add noexcept-specifier. * testsuite/20_util/tuple/apply/2.cc: New test. * testsuite/20_util/tuple/make_from_tuple/2.cc: New test. diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 980dd6d6270..dd966b3a0bc 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -1591,6 +1591,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } #if __cplusplus >= 201703L + + // Unpack a std::tuple into a type trait and use its value. + // For cv std::tuple<_Up> the result is _Trait<_Tp, cv _Up...>::value. + // For cv std::tuple<_Up>& the result is _Trait<_Tp, cv _Up&...>::value. + // Otherwise the result is false (because we don't know if std::get throws). + template<template<typename...> class _Trait, typename _Tp, typename _Tuple> + inline constexpr bool __unpack_std_tuple = false; + + template<template<typename...> class _Trait, typename _Tp, typename... _Up> + inline constexpr bool __unpack_std_tuple<_Trait, _Tp, tuple<_Up...>> + = _Trait<_Tp, _Up...>::value; + + template<template<typename...> class _Trait, typename _Tp, typename... _Up> + inline constexpr bool __unpack_std_tuple<_Trait, _Tp, tuple<_Up...>&> + = _Trait<_Tp, _Up&...>::value; + + template<template<typename...> class _Trait, typename _Tp, typename... _Up> + inline constexpr bool __unpack_std_tuple<_Trait, _Tp, const tuple<_Up...>> + = _Trait<_Tp, const _Up...>::value; + + template<template<typename...> class _Trait, typename _Tp, typename... _Up> + inline constexpr bool __unpack_std_tuple<_Trait, _Tp, const tuple<_Up...>&> + = _Trait<_Tp, const _Up&...>::value; + # define __cpp_lib_apply 201603 template <typename _Fn, typename _Tuple, size_t... _Idx> @@ -1604,6 +1628,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Fn, typename _Tuple> constexpr decltype(auto) apply(_Fn&& __f, _Tuple&& __t) + noexcept(__unpack_std_tuple<is_nothrow_invocable, _Fn, _Tuple>) { using _Indices = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>; @@ -1622,6 +1647,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template <typename _Tp, typename _Tuple> constexpr _Tp make_from_tuple(_Tuple&& __t) + noexcept(__unpack_std_tuple<is_nothrow_constructible, _Tp, _Tuple>) { return __make_from_tuple_impl<_Tp>( std::forward<_Tuple>(__t), diff --git a/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc b/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc new file mode 100644 index 00000000000..aa5968f397f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/apply/2.cc @@ -0,0 +1,62 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++17" } +// { dg-do compile { target c++17 } } + +// Test noexcept-specifier on std::apply + +#include <tuple> + +using std::tuple; +using std::declval; + +void f1(); + +static_assert( !noexcept(apply(f1, declval<tuple<>>())) ); +static_assert( !noexcept(apply(f1, declval<tuple<>&>())) ); +static_assert( !noexcept(apply(f1, declval<const tuple<>>())) ); +static_assert( !noexcept(apply(f1, declval<const tuple<>&>())) ); + +void f2() noexcept; + +static_assert( noexcept(apply(f2, declval<tuple<>>())) ); +static_assert( noexcept(apply(f2, declval<tuple<>&>())) ); +static_assert( noexcept(apply(f2, declval<const tuple<>>())) ); +static_assert( noexcept(apply(f2, declval<const tuple<>&>())) ); + +struct F3 { + void operator()(int&); + void operator()(int&&) noexcept; + void operator()(const int&) noexcept; + void operator()(const int&&); +} f3; + +static_assert( noexcept(apply(f3, declval<tuple<int>>())) ); +static_assert( !noexcept(apply(f3, declval<tuple<int>&>())) ); +static_assert( !noexcept(apply(f3, declval<const tuple<int>>())) ); +static_assert( noexcept(apply(f3, declval<const tuple<int>&>())) ); + +struct F4 { + void operator()(int&, const int&); + void operator()(int&&, int&&) noexcept; +} f4; + +static_assert( noexcept(apply(f4, declval<tuple<int, int>>())) ); +static_assert( !noexcept(apply(f4, declval<tuple<int, int>&>())) ); +static_assert( !noexcept(apply(f4, declval<tuple<int&, const int>>())) ); +static_assert( !noexcept(apply(f4, declval<tuple<int, const int>&>())) ); diff --git a/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc new file mode 100644 index 00000000000..18a9466ef42 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/make_from_tuple/2.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++17" } +// { dg-do compile { target c++17 } } + +// Test noexcept-specifier on std::make_from_tuple + +#include <tuple> + +using std::make_from_tuple; +using std::tuple; +using std::declval; + +struct T1 { T1(); }; + +static_assert( !noexcept(make_from_tuple<T1>(declval<tuple<>>())) ); +static_assert( !noexcept(make_from_tuple<T1>(declval<tuple<>&>())) ); +static_assert( !noexcept(make_from_tuple<T1>(declval<const tuple<>>())) ); +static_assert( !noexcept(make_from_tuple<T1>(declval<const tuple<>&>())) ); + +struct T2 { }; + +static_assert( noexcept(make_from_tuple<T2>(declval<tuple<>>())) ); +static_assert( noexcept(make_from_tuple<T2>(declval<tuple<>&>())) ); +static_assert( noexcept(make_from_tuple<T2>(declval<const tuple<>>())) ); +static_assert( noexcept(make_from_tuple<T2>(declval<const tuple<>&>())) ); + +struct T3 { + T3(int&); + T3(int&&) noexcept; + T3(const int&) noexcept; + T3(const int&&); +}; + +static_assert( noexcept(make_from_tuple<T3>(declval<tuple<int>>())) ); +static_assert( !noexcept(make_from_tuple<T3>(declval<tuple<int>&>())) ); +static_assert( !noexcept(make_from_tuple<T3>(declval<const tuple<int>>())) ); +static_assert( noexcept(make_from_tuple<T3>(declval<const tuple<int>&>())) ); + +struct T4 { + T4(int&, const int&); + T4(int&&, int&&) noexcept; +}; + +static_assert( noexcept(make_from_tuple<T4>(declval<tuple<int, int>>())) ); +static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int, int>&>())) ); +static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int&, const int>>())) ); +static_assert( !noexcept(make_from_tuple<T4>(declval<tuple<int, const int>&>())) );