Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions stl/inc/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,26 @@ struct _Tuple_perfect_val<tuple<_Ty0, _Ty1, _Ty2>, _Uty0, _Uty1, _Uty2>

template <class _Ty>
struct _Tuple_val { // stores each value in a tuple
// Use of remove_cv_t corresponds to N5008 [allocator.uses.construction]/5.
using _Dty = remove_cv_t<_Ty>;

constexpr _Tuple_val() : _Val() {}

template <class _Other>
constexpr _Tuple_val(_Other&& _Arg) : _Val(_STD forward<_Other>(_Arg)) {}

template <class _Alloc, class... _Other, enable_if_t<!uses_allocator_v<_Ty, _Alloc>, int> = 0>
template <class _Alloc, class... _Other, enable_if_t<!uses_allocator_v<_Dty, _Alloc>, int> = 0>
constexpr _Tuple_val(const _Alloc&, allocator_arg_t, _Other&&... _Arg) : _Val(_STD forward<_Other>(_Arg)...) {}

template <class _Alloc, class... _Other,
enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>,
enable_if_t<conjunction_v<_STD uses_allocator<_Dty, _Alloc>,
_STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>,
int> = 0>
constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg)
: _Val(allocator_arg, _Al, _STD forward<_Other>(_Arg)...) {}

template <class _Alloc, class... _Other,
enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>,
enable_if_t<conjunction_v<_STD uses_allocator<_Dty, _Alloc>,
_STD negation<_STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>>,
int> = 0>
constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg)
Expand Down
121 changes: 121 additions & 0 deletions tests/std/tests/Dev11_0343056_pair_tuple_ctor_sfinae/test.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <cstddef>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

#if _HAS_CXX20
#define CONSTEXPR20 constexpr
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
#define CONSTEXPR20 inline
#endif // ^^^ !_HAS_CXX20 ^^^

using namespace std;

Expand Down Expand Up @@ -50,6 +60,111 @@ void takes_tuple(tuple<Neutron, Neutron>) {}

void test_alloc();

template <class T>
class payloaded_allocator {
private:
int payload = 0;

public:
payloaded_allocator() = default;

constexpr explicit payloaded_allocator(int n) noexcept : payload{n} {}

template <class U>
constexpr explicit payloaded_allocator(payloaded_allocator<U> a) noexcept : payload{a.get_payload()} {}

template <class U>
friend constexpr bool operator==(payloaded_allocator x, payloaded_allocator<U> y) noexcept {
return x.payload == y.payload;
}

#if !_HAS_CXX20
template <class U>
friend constexpr bool operator!=(payloaded_allocator x, payloaded_allocator<U> y) noexcept {
return !(x == y);
}
#endif // !_HAS_CXX20

using value_type = T;

CONSTEXPR20 T* allocate(size_t n) {
return allocator<T>{}.allocate(n);
}

CONSTEXPR20 void deallocate(T* p, size_t n) {
return allocator<T>{}.deallocate(p, n);
}

constexpr int get_payload() const noexcept {
return payload;
}
};

template <class T, class A>
class vector_holder {
public:
vector_holder() = default;
template <class... Args>
CONSTEXPR20 explicit vector_holder(Args&&... args, const A& alloc) : vec_(args..., alloc) {}

CONSTEXPR20 A get_allocator() const noexcept {
return vec_.get_allocator();
}

private:
vector<T, A> vec_;
};

template <class T, class A>
struct std::uses_allocator<vector_holder<T, A>, A> : true_type {};

class payload_taker {
public:
payload_taker() = default;

template <class T>
constexpr explicit payload_taker(const payloaded_allocator<T>& alloc) noexcept : payload{alloc.get_payload()} {}

constexpr int get_payload() const noexcept {
return payload;
}
int get_payload() const volatile noexcept {
return payload;
}

private:
int payload = 0;
};

template <class T>
struct std::uses_allocator<payload_taker, payloaded_allocator<T>> : true_type {};

// LWG-3677 "Is a cv-qualified pair specially handled in uses-allocator construction?"
CONSTEXPR20 bool test_lwg3677() {
using my_allocator = payloaded_allocator<int>;

tuple<payload_taker> t1{allocator_arg, my_allocator{42}};
assert(get<0>(t1).get_payload() == 42);
tuple<const payload_taker> t2{allocator_arg, my_allocator{84}};
assert(get<0>(t2).get_payload() == 84);

tuple<vector_holder<int, my_allocator>> t3{allocator_arg, my_allocator{126}};
assert(get<0>(t3).get_allocator().get_payload() == 126);
tuple<const vector_holder<int, my_allocator>> t4{allocator_arg, my_allocator{168}};
assert(get<0>(t4).get_allocator().get_payload() == 168);

return true;
}

void test_lwg3677_volatile() {
using my_allocator = payloaded_allocator<int>;

tuple<volatile payload_taker> t5{allocator_arg, my_allocator{210}};
assert(get<0>(t5).get_payload() == 210);
tuple<const volatile payload_taker> t6{allocator_arg, my_allocator{252}};
assert(get<0>(t6).get_payload() == 252);
}

int main() {
B* b = nullptr;
Y* y = nullptr;
Expand Down Expand Up @@ -129,6 +244,12 @@ int main() {


test_alloc();

test_lwg3677();
#if _HAS_CXX20
static_assert(test_lwg3677());
#endif // _HAS_CXX20
test_lwg3677_volatile();
}

struct Meow {
Expand Down