Details
Diff Detail
- Repository
- rCXX libc++
Event Timeline
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
47 | We have a macro called ASSERT_SAME_TYPE. You get it by including test_macros.h. Easier to read than std::is_same_v<... |
test/std/containers/associative/map/map.cons/deduct.fail.cpp | ||
---|---|---|
40 | It would be good if there were comments here explaining why this should fail. |
test/std/containers/associative/map/map.cons/deduct.fail.cpp | ||
---|---|---|
45 | Please say why you expect these tests to fail. |
Use ASSERT_SAME_TYPE.
Better comments in .fail.cpp.
Fix some whitespace in my comments.
@mclow.lists ping!
include/iterator | ||
---|---|---|
541 | Incidentally, I have no opinion on whether this #ifndef is appropriate. It shouldn't break anything to define these aliases unconditionally, except that then I'd probably have to switch the usings to typedefs. |
include/map | ||
---|---|---|
1474 | So those enable_ifs are required to avoid colliding with the map(initializer_list<pair<_Key, _Tp>>, _Allocator) deduction guide, correct? |
include/map | ||
---|---|---|
1474 | Yes, that's right. They correspond directly with the SFINAE requirements in |
Here's a bunch of minor stylistic and comment nits.
I'm irked by the bit typedef typename __identity<_Allocator>::type allocator_type; in the definition of map/multimap.
What problem is that solving?
test/std/containers/associative/multimap/multimap.cons/deduct.fail.cpp | ||
---|---|---|
60 | What's going on here? | |
65 | Missing motivation for this bit. | |
test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp | ||
39 | All though this test, you have a mixture of pair<int, long> and pair<const int, long>. Are these changes relevant? If you need them for the equals calls, you can define a custom comparison: template <class T1, class T2> struct pair_eq { bool operator()(const std::pair< T1, T2> &rhs, const std::pair< T1, T2> &lhs) const { return lhs.first == rhs.first && lhs.second == rhs.second; } bool operator()(const std::pair<const T1, const T2> &rhs, const std::pair<const T1, const T2> &lhs) const { return lhs.first == rhs.first && lhs.second == rhs.second; } bool operator()(const std::pair< T1, const T2> &rhs, const std::pair<const T1, const T2> &lhs) const { return lhs.first == rhs.first && lhs.second == rhs.second; } bool operator()(const std::pair<const T1, const T2> &rhs, const std::pair< T1, const T2> &lhs) const { return lhs.first == rhs.first && lhs.second == rhs.second; } }; and then call I'm not 100% sure that this is a win, but it might be. | |
40 | Alternately, you could define a couple of typedefs: using P = std::pair<int, long>; using PC = std::pair<const int, long>; | |
45 | This should be const, too. (I know that you don't ever change this, but ...) | |
77 | A comment here "braces instead of parens" would help a lot. | |
91 | typedef using P = std::pair<int, long>; would make this clearer. | |
111 | Why is this not P expected_m[] = ...? |
I'm irked by the typedef typename __identity<_Allocator>::type allocator_type; in the definition of map/multimap. What problem is that solving?
Unfortunately, if you don't place a "deduction firewall" between allocator_type and _Allocator, then the deduction guides implicitly generated from the unconstrained constructors of map/multimap that take an allocator_type parameter will happily match and deduce e.g. _Allocator=std::less<int>, which then blows up with a hard error trying to instantiate allocator_traits<std::less<int>>. We need to place that firewall in order to nerf the implicitly generated deduction guides, so that our explicit deduction guides can take priority.
Easy experiment: remove the firewall and compile the new test files. If I'm not mistaken, you'll see horrible error messages.
test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp | ||
---|---|---|
40 | This sounds good. | |
91 | Hmm. I seem to recall that I specifically didn't want to nail down what kind of pair was being CTAD'ed — the idea was to keep this specific code (a CTAD multimap of pair{x,y}s) working even if the CTAD guides on either multimap or pair are changed in the future. Other tests would handle "a CTAD multimap of pair<A,B>(x,y)s" and "a CTAD multimap of {x,y}s." I am unsure whether multimap{ pair{a,b}, pair{c,d} } can ever produce a different type from multimap{ pair<A,B>{a,b}, pair<A,B>{c,d} }. I am pretty sure that those can produce a different type from multimap{ {a,b}, {c,d} }. | |
111 | Will change to const PC expected_m[] = .... |
test/std/containers/associative/multimap/multimap.cons/deduct.fail.cpp | ||
---|---|---|
60 | The reason it's commented out is that the error message that's actually printed is a hard error from inside include/map and I don't know how to write an expected-error line for that. Is that possible? Here's the error as printed: File $ROOT/llvm/projects/libcxx/include/map Line 1671: static_assert failed due to requirement 'is_same<std::__1::pair<int, long>, std::__1::pair<const int, long> >::value' "Allocator::value_type must be same type as value_type" | |
test/std/containers/associative/multimap/multimap.cons/deduct.pass.cpp | ||
91 | I found the "CTAD multimap of {x,y}s" in deduct.fail.cpp. :) |
Review comments on the unit tests.
Also, it looks like my initial upload didn't actually include the LWG3025 fix? The fix involves the remove_const that's now in most of the deduction guides (and a new test file to test the change).
test/std/containers/associative/multimap/multimap.cons/deduct_const.pass.cpp | ||
---|---|---|
123 ↗ | (On Diff #198084) | BTW, if you told me to remove these last two tests (which test std::multimap<const int&, long>), I would do so. Note that std::map cannot support a reference-typed _Key because then try_emplace would give multiple declaration errors: const _Key& and _Key&& would be the same type. |
include/map | ||
---|---|---|
1474 | Those requirements are for container adapters -- do we have similar requirements for map and set? |
include/map | ||
---|---|---|
1474 | Oops, you're absolutely right. Yes we do; the requirements for map and set are in |
This looks good to me, with a few nits.
include/map | ||
---|---|---|
2124 | You're very consistent here with qualifying remove_const, and nothing else. Why? | |
test/std/containers/associative/map/map.cons/deduct.fail.cpp | ||
63 | Why is this test commented out? | |
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
88 | This works? I would think that it should not. We rebind the allocator internally into something else, but we should probably reject this. [But not in this patch] | |
test/std/containers/associative/multimap/multimap.cons/deduct.fail.cpp | ||
60 | See test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp for an example. // expected-error-re@ map:* {{static_assert failed{{( due to requirement '.*')?}} "Allocator::value_type must be same type as value_type"}} |
test/std/containers/associative/multimap/multimap.cons/deduct_const.pass.cpp | ||
---|---|---|
123 ↗ | (On Diff #198084) | I'll look into this. |
I remember looking at this and I was fine with it. LGTM if @mclow.lists is fine with it.
include/map | ||
---|---|---|
2124 | No reason. Removed. | |
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
88 | This is me getting a jump start on P1518 Stop overconstraining allocators in container deduction guides. Since std::map<int, long> m(source, std::allocator<int>()); works (without CTAD), Mike Spertus (as of February) and I believe that std::map m(source, std::allocator<int>()); should work with CTAD as well. |
Almost there - and I'm on the hook for comments on the reference stuff.
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 | I don't believe that either should work. std::vector<long, std::allocator<int>> m; long l[] = {1L }; std::vector v(std::begin(l), std::end(l), std::allocator<int>()); The fact that it works today for map and set is due to insufficient checking in libc++ |
test/std/containers/associative/multimap/multimap.cons/deduct_const.pass.cpp | ||
---|---|---|
123 ↗ | (On Diff #198084) | I would say drop these two tests. We don't have any other map tests that use reference keys. |
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 | std::vector<long, std::allocator<int>> m; is not expected to work, because it avoids CTAD and explicitly instantiates vector with bad template parameters. std::vector v(std::begin(l), std::end(l), std::allocator<int>()) is not expected to work either (although there's a slightly stronger case for allowing it to work), because the value type of the iterator is different from the value type of the-allocator-which-is-deduced-by-CTAD. (Since the allocator is deduced, there's arguably a case for CTAD to trust the allocator's value_type and ignore the irrelevant value type of the iterator. That is, arguably, CTAD could do the same thing that std::vector<int, std::allocator<int>> v(std::begin(l), std::end(l), std::allocator<int>()) does.) But in std::vector w(source, std::allocator<int>()), CTAD knows we're using the allocator-aware copy constructor of vector. That constructor deduces the allocator type from the first parameter — the vector it's making a copy of — not from the second parameter. The only requirement is that the passed-in allocator argument should be implicitly convertible to the deduced allocator type. Which in this case, it is. I'm willing to #if 0 out this test for now, but I am also willing to keep debating it until you see the P1518 light. ;) | |
test/std/containers/associative/multimap/multimap.cons/deduct_const.pass.cpp | ||
123 ↗ | (On Diff #198084) | Will do. |
Address review comments. (Remove the two tests involving a multimap-of-reference-type.)
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 | I think we're discussing different things here. I'm talking about the general container requirements, the very first line in Table 62, which says:
As far as I can tell, P1518 doesn't change that at all. |
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 | I think you think we're discussing different things, but we're not. ;) std::map<int, long> source; std::map m(source, std::allocator<int>()); ASSERT_SAME_TYPE(decltype(m), decltype(source)); I claim (equivalently, P1518 claims) that the variable definition on line 2 creates a variable m of type std::map<int, long>. This is "obvious" because it is calling the allocator-aware constructor map(const map&, allocator). We're using CTAD here, but we could just as well put the deduced template arguments back in place: std::map<int, long> m(source, std::allocator<int>()); This is fine. The second constructor argument, the one that provides a "value" for the new map's allocator, is implicitly converted from std::allocator<int> to std::map<int, long>::allocator_type. You might say "Shouldn't the second parameter's type be forced to exactly match std::map<int, long>::allocator_type?" but no, that's not how it works without CTAD, so it's not how it should work with CTAD either. In particular, we would like the following to work: std::pmr::map<int, long> source; std::map m(source, std::pmr::new_delete_resource()); ASSERT_SAME_TYPE(decltype(m), decltype(source)); // C++2a only: requires CTAD to see through typedefs std::pmr::map m2(source, std::pmr::new_delete_resource()); ASSERT_SAME_TYPE(decltype(m2), decltype(source)); That can only work (AFAIK) if you don't allow CTAD to "get confused" by the actual argument type of that second argument. All we require is that the actual argument should be something convertible to allocator_type (which would be the requirement in the absence of CTAD). |
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 |
I think that we should remove this test, commit the rest of this patch, and then continue this debate if/when P1518 lands. Tests that are commented out are always a question for the reader: "Why is this test commented out?" |
Use the original decltype(source)::allocator_type instead of std::allocator<int> in the allocator-aware-copy-constructor test. So we don't insist on P1518R0 support (yet).
@mclow.lists: I think everything's resolved and this is ready for you (or someone; not me) to land. Once it's landed, I assume the next step will be to resume review on D58582 (set) and/or D58590 (unordered_map).
test/std/containers/associative/map/map.cons/deduct.pass.cpp | ||
---|---|---|
88 | Okay. Not removed, but modified to use the exact allocator_type, which we both agree should work regardless. |
Marshall, std/containers/associative/map/map.cons/deduct_const.pass.cpp test seems to be reliably failing from commit one on both Gentoo Linux and NetBSD. Could you please look at the classical example of totally cryptic C++ error? ;-)
- Add an __identity<T>::type "firewall" on the comparator type as well as on the allocator type. (This fixes the Gentoo/NetBSD test failure. Essentially, my patch had been accidentally relying on a clang bug which caused the implicit deduction guides to be totally ignored in this case. When the clang bug got fixed, my patch broke. Adding the "firewall" fixes my patch AFAICT.)
- Use enable_if_t and remove_const_t instead of the longer C++11-friendly names. I assume that since the deduction guides are compiled only in C++17 library mode, they are allowed to rely on C++14-and-later library features. However, I can put it back the way it was if someone disagrees with my assumption.
Incidentally, I have no opinion on whether this #ifndef is appropriate. It shouldn't break anything to define these aliases unconditionally, except that then I'd probably have to switch the usings to typedefs.