On Tue, 23 Aug 2022, Jonathan Wakely wrote: > On Tue, 23 Aug 2022 at 02:35, Patrick Palka via Libstdc++ > <libstd...@gcc.gnu.org> wrote: > > > > P2321R2 adds new conditionally explicit constructors to std::tuple which > > we'll concisely implement in a subsequent patch using explicit(bool), like > > in our C++20 std::pair implementation. But before we can do that, this > > patch first adds members to _TupleConstraints that test for constructibility > > and convertibility separately; we'll use the first in the new constructors' > > constraints, and the second in their explicit specifier. > > > > In passing, this patch also redefines the existing predicates > > __is_ex/implicitly_constructible in terms of these new members. This > > seems to reduce compile time and memory usage by about 10% for large > > Nice. > > > tuples when using the relevant constructors constrained by > > _Explicit/_ImplicitCtor (since we no longer have to redundantly expand > > and process is_constructible<_Types, _UTypes>... twice for each pair of > > such constructors). In order to retain maximal short circuiting, do > > this only when constexpr if is available. > > Would we get similar benefits for C++11 and C++14 by doing: > > return __and_<__and_<is_constructible<_Types, _UTypes>...>, > __and_<is_convertible<_UTypes, _Types>...> > >::value; > > This is slightly more work in total, but if we have __and_<A,B> and > __and_<A,__not_<B>> then the A and B instantiations will be cached and > can be reused.
Good idea, it seems we get pretty much the same 10% improvement for C++11/14 with this approach. I reckon we might as well then define __convertible and __constructible as alias templates instead of as variable templates and use them unconditionally in __is_im/explicitly_constructible for benefit of all language versions. Using constexpr if instead of the outer __and_ seems to yield a marginal improvement at best and __and_v is currently just __and_::value, so it doesn't seem worth it to have different definitions for C++17 at least for now. What do you think about the following? -- >8 -- libstdc++-v3/ChangeLog: * include/std/tuple (_TupleConstraints::__convertible): Define. (_TupleConstraints::__constructible): Likewise. (_TupleConstraints::__is_explicitly_constructible): Redefine this in terms of __convertible and __constructible. (_TupleConstraints::__is_implicitly_constructible): Likewise. --- libstdc++-v3/include/std/tuple | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 6d0060a191c..f8f48ccc370 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -553,15 +553,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<bool, typename... _Types> struct _TupleConstraints { + template<typename... _UTypes> + using __constructible = __and_<is_constructible<_Types, _UTypes>...>; + + template<typename... _UTypes> + using __convertible = __and_<is_convertible<_UTypes, _Types>...>; + // Constraint for a non-explicit constructor. // True iff each Ti in _Types... can be constructed from Ui in _UTypes... // and every Ui is implicitly convertible to Ti. template<typename... _UTypes> static constexpr bool __is_implicitly_constructible() { - return __and_<is_constructible<_Types, _UTypes>..., - is_convertible<_UTypes, _Types>... - >::value; + return __and_<__constructible<_UTypes...>, + __convertible<_UTypes...>>::value; } // Constraint for a non-explicit constructor. @@ -570,9 +575,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename... _UTypes> static constexpr bool __is_explicitly_constructible() { - return __and_<is_constructible<_Types, _UTypes>..., - __not_<__and_<is_convertible<_UTypes, _Types>...>> - >::value; + return __and_<__constructible<_UTypes...>, + __not_<__convertible<_UTypes...>>>::value; } static constexpr bool __is_implicitly_default_constructible() -- 2.37.2.382.g795ea8776b