[libcxx-commits] [PATCH] D65161: [libc++] Implement LWG 2510

Arthur O'Dwyer via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 24 13:25:38 PDT 2019


Quuxplusone added inline comments.


================
Comment at: libcxx/include/tuple:959
 #ifndef _LIBCPP_HAS_NO_DEDUCTION_GUIDES
+tuple() -> tuple<>;
+
----------------
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.


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