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
 - rL LLVM
 
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 | ||
|---|---|---|
| 959 ↗ | (On Diff #211534) | @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 | ||
|---|---|---|
| 959 ↗ | (On Diff #211534) | 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 | ||
|---|---|---|
| 959 ↗ | (On Diff #211534) | Woah, thanks a lot for the analysis! Interesting, but indeed I think adding the deduction guides is cleaner.  | 
| libcxx/include/type_traits | ||
|---|---|---|
| 2819 ↗ | (On Diff #214676) | 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 | ||
| 10 ↗ | (On Diff #214676) | I think we should do our best to support this test (and the similar ones below) in C++03, since this is a DR.  | 
| libcxx/test/std/utilities/memory/allocator.tag/allocator_arg.fail.cpp | ||
|---|---|---|
| 10 ↗ | (On Diff #214676) | That's probably a good idea. See my comment below. There are a few other tests, maybe we should update/enable those too?  | 
| 19 ↗ | (On Diff #214676) | If we are keeping this test we should update this function to return std:: allocator_arg_t ().  | 
this looks fine to me now (with a nit).
| libcxx/include/type_traits | ||
|---|---|---|
| 2823 ↗ | (On Diff #221571) | 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 | ||
|---|---|---|
| 19 ↗ | (On Diff #214676) | 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 | ||
|---|---|---|
| 19 ↗ | (On Diff #214676) | 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.  |