[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