[libcxx-commits] [PATCH] D65161: [libc++] Implement LWG 2510
Louis Dionne via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 24 14:47:47 PDT 2019
ldionne marked an inline comment as done.
ldionne added inline comments.
================
Comment at: libcxx/include/tuple:959
#ifndef _LIBCPP_HAS_NO_DEDUCTION_GUIDES
+tuple() -> tuple<>;
+
----------------
Quuxplusone wrote:
> ldionne wrote:
> > @Quuxplusone Any idea why this is needed? Otherwise, I get:
> >
> >
> > ```
> > <snip>/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/implicit_deduction_guides.pass.cpp:125:16: error: no viable constructor or deduction guide for deduction of template arguments of 'tuple'
> > std::tuple t1{};
> > ^
> > <snip>/libcxx/include/tuple:643:5: note: candidate template ignored: substitution failure [with _Tp = <>, _Dummy = true]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > tuple()
> > ^
> > <snip>/libcxx/include/tuple:650:5: note: candidate template ignored: substitution failure [with _Tp = <>, _Dummy = true]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > tuple()
> > ^
> > <snip>/libcxx/include/tuple:680:5: note: candidate template ignored: substitution failure [with _Tp = <>, _Dummy = true]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > tuple(const _Tp& ... __t) _NOEXCEPT_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
> > ^
> > <snip>/libcxx/include/tuple:698:14: note: candidate template ignored: substitution failure [with _Tp = <>, _Dummy = true]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > explicit tuple(const _Tp& ... __t) _NOEXCEPT_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
> > ^
> > <snip>/libcxx/include/tuple:762:9: note: candidate template ignored: substitution failure [with _Tp = <>, _Up = <>]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > tuple(_Up&&... __u)
> > ^
> > <snip>/libcxx/include/tuple:795:9: note: candidate template ignored: substitution failure [with _Tp = <>, _Up = <>]: cannot reference member of primary template because deduced class template specialization 'tuple<>' is an explicit specialization
> > tuple(_Up&&... __u)
> > ^
> > <snip>/libcxx/include/__tuple:159:52: note: candidate function template not viable: requires 1 argument, but 0 were provided
> > template <class ..._Tp> class _LIBCPP_TEMPLATE_VIS tuple;
> > ^
> > <snip>/libcxx/include/tuple:653:5: note: candidate function template not viable: requires 1 argument, but 0 were provided
> > tuple(tuple const&) = default;
> > ^
> > <snip>/libcxx/include/tuple:654:5: note: candidate function template not viable: requires 1 argument, but 0 were provided
> > tuple(tuple&&) = default;
> > ^
> > <snip>/libcxx/include/tuple:664:5: note: candidate function template not viable: requires 2 arguments, but 0 were provided
> > tuple(_AllocArgT, _Alloc const& __a)
> > ^
> > <snip>/libcxx/include/tuple:716:7: note: candidate function template not viable: requires at least 2 arguments, but 0 were provided
> > tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
> > ^
> > <snip>/libcxx/include/tuple:736:7: note: candidate function template not viable: requires at least 2 arguments, but 0 were provided
> > tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
> > ^
> > <snip>/libcxx/include/tuple:822:9: note: candidate function template not viable: requires at least 2 arguments, but 0 were provided
> > tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
> > ^
> > <snip>/libcxx/include/tuple:842:9: note: candidate function template not viable: requires at least 2 arguments, but 0 were provided
> > tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
> > ^
> > <snip>/libcxx/include/tuple:852:9: note: candidate function template not viable: requires single argument '__t', but no arguments were provided
> > tuple(_Tuple&& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, _Tuple>::value))
> > ^
> > <snip>/libcxx/include/tuple:857:9: note: candidate function template not viable: requires single argument '__t', but no arguments were provided
> > tuple(const _Tuple& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, const _Tuple&>
> >
> > [...]
> > ```
> I got it! :) https://godbolt.org/z/NGrj_6
>
> ```
> template<class...> struct Tuple {
> static constexpr bool Example = true;
> template<bool = Example> Tuple();
> };
> template<> struct Tuple<> {};
>
> Tuple t;
> ```
> ```
> error: no viable constructor or deduction guide for deduction of template arguments of 'Tuple'
> Tuple t;
> ^
> note: candidate template ignored: substitution failure [with $0 = <>]: cannot reference member of primary template because deduced class template specialization 'Tuple<>' is an explicit specialization
> template<bool = Example> Tuple();
> ~~~~~~~ ^
> note: candidate function template not viable: requires 1 argument, but 0 were provided
> template<class...> struct Tuple {
> ^
> ```
> Clang, probably rightly — although I'm not sure — considers only implicit deduction guides that are created from the //actual constructors// of the primary template. So, when it sees a constructor template, it needs to figure out whether that template corresponds to an actual constructor, or a SFINAE'd-away constructor (because if the latter, then it should not make a deduction guide for it). However, it needs to do that SFINAE check without actually instantiating the primary template (because it's simply not allowed to instantiate the primary template when a suitable specialization exists). So, if the SFINAE-space-validity of the constructor template is dependent on any member of the primary template — such as `Tuple<Ts...>::Example` in this example, or `tuple<_Tp...>::_CheckArgsConstructor` in libc++ — then the compiler can't safely figure out if the constructor template corresponds to an actual constructor or not, and so the compiler must throw it out. (Clang treats this as a "substitution failure," but intuitively I don't think that's exactly correct. It's more like, the compiler doesn't even know whether substitution is going to fail or not.)
>
> Conclusion: You //could// solve the original cascade of errors by making all those helper structs (`std::tuple<_Tp...>::_CheckArgsConstructor` etc) into non-member struct templates (`std::_TupleCheckArgsConstructor<_Tp>` etc). Adding the deduction guide seems much simpler, and also follows the letter of the standard, which is a good thing.
Woah, thanks a lot for the analysis! Interesting, but indeed I think adding the deduction guides is cleaner.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D65161/new/
https://reviews.llvm.org/D65161
More information about the libcxx-commits
mailing list