LWG2510 makes tag types like allocator_arg_t explicitly default
constructible instead of implicitly default constructible. It also
makes the constructors for std::pair and std::tuple conditionally
explicit based on the explicit-ness of the default constructibility
for the pair/tuple's elements.
Details
- Reviewers
mclow.lists EricWF zoecarver - Commits
- rGe16f2cb67892: [libc++] Take 2: Implement LWG 2510
rL372983: [libc++] Take 2: Implement LWG 2510
rCXX372983: [libc++] Take 2: Implement LWG 2510
rCXX372777: [libc++] Implement LWG 2510
rG95411dd426e6: [libc++] Implement LWG 2510
rL372777: [libc++] Implement LWG 2510
Diff Detail
- Repository
- rG LLVM Github Monorepo
- Build Status
Buildable 38602 Build 38601: arc lint + arc unit
Event Timeline
Add deduction guide for tuple<>. Otherwise, Clang seems to be unable to deduce
std::tuple t{} because the deduction guide leads to an explicit specialization.
libcxx/include/tuple | ||
---|---|---|
975 | @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&> [...] |
libcxx/include/tuple | ||
---|---|---|
975 | 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. |
libcxx/include/tuple | ||
---|---|---|
975 | Woah, thanks a lot for the analysis! Interesting, but indeed I think adding the deduction guides is cleaner. |
libcxx/include/type_traits | ||
---|---|---|
2819 | If you're thinking about the use of decltype, it's fine to assume that we have it in C++03 mode too. However, the implicit construction via {} is indeed a C++11 feature that's not supported in C++03, so I'll need to find another way to perform the check. I think we want to find a way to do the check in C++03 instead of always having __is_implicitly_default_constructible<T>::value == false because that will change the explicit-ness of things like std::pair in C++03 vs C++11, which is a notable change. Also, the intent of a DR is to have it back-applied so it would be nice if we could implement the issue resolution even in C++03. I'll admit I can't think of any way to implement the check in C++03 though. | |
libcxx/test/std/utilities/memory/allocator.tag/allocator_arg.fail.cpp | ||
16 | I think we should do our best to support this test (and the similar ones below) in C++03, since this is a DR. |
this looks fine to me now (with a nit).
libcxx/include/type_traits | ||
---|---|---|
2823 | Nit: This comment doesn't really match the #ifndef _LIBCPP_CXX03_LANG that starts the block. |
This was reverted in r372832. I'm waiting to see the resolution of https://bugs.llvm.org/show_bug.cgi?id=43454 before I know what to do.
libcxx/test/std/utilities/memory/allocator.tag/allocator_arg.fail.cpp | ||
---|---|---|
25 | The whole point of this test is to exercise the error caused by the non-explicit copy-initialization. I'm not sure I understand your comment. |
libcxx/test/std/utilities/memory/allocator.tag/allocator_arg.fail.cpp | ||
---|---|---|
25 | I think I was worried about C++03 mode. Now that this has been updated to be unsupported in C++03, don't worry about it. |
@Quuxplusone Any idea why this is needed? Otherwise, I get: