* [RFC] Deprecate non-standard constructors in std::pair
@ 2021-04-07 12:30 Jonathan Wakely
2021-04-07 12:41 ` Ville Voutilainen
0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 12:30 UTC (permalink / raw)
To: libstdc++
This variation on the example in https://wg21.link/lwg811 is supposed
to be ill-formed:
pair<char*, unique_ptr<int>> p(0, std::make_unique<int>(0));
This cannot use the pair(U1&&, U2&&) constructor because it deduces
int for U1, which isn't convertible to char*. But it also cannot use
the pair(const first_type&, const second_type&) constructor, because
second_type is move-only.
It works in libstdc++ because extra non-standard constructors were
added by https://gcc.gnu.org/PR40925 when implementing LWG 811:
template<typename U1, /* constraints */>
explicit(/*...*/)
pair(U1&&, const second_type&);
template<typename U2, /* constraints */>
explicit(/*...*/)
pair(const first_type&, U2&&);
I think we should deprecate these extra constructors.
Unless I'm mistaken, those non-standard constructors have two uses:
- Support literal 0 as a constructor argument for a pointer type.
Such code should use nullptr instead. It exists to solve the problem
of 0 not deducing as a pointer type.
- Support {} as a constructor argument for a move-only type. I don't
think this was an intentional design choice (we don't test for it)
but it does work. Adopting https://wg21.link/p1951 for C++23 will
make that work anyway.
I propose that we deprecate the constructors for C++11/14/17/20 in
stage 1, and do not support them at all in C++23 mode once P1951 is
supported. I have a patch which I'll send in stage 1 (it also uses
C++20 concepts to simplify std::pair and fix PR 97930).
After a period of deprecation we could remove them, and support P1951
for -std=gnu++11/14/17/20 too so that {} continues to work.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 12:30 [RFC] Deprecate non-standard constructors in std::pair Jonathan Wakely
@ 2021-04-07 12:41 ` Ville Voutilainen
2021-04-07 12:46 ` Jonathan Wakely
0 siblings, 1 reply; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 12:41 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: libstdc++
On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
> I propose that we deprecate the constructors for C++11/14/17/20 in
> stage 1, and do not support them at all in C++23 mode once P1951 is
> supported. I have a patch which I'll send in stage 1 (it also uses
> C++20 concepts to simplify std::pair and fix PR 97930).
>
> After a period of deprecation we could remove them, and support P1951
> for -std=gnu++11/14/17/20 too so that {} continues to work.
The proposal sounds good to me.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 12:41 ` Ville Voutilainen
@ 2021-04-07 12:46 ` Jonathan Wakely
2021-04-07 16:59 ` Jonathan Wakely
0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 12:46 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++
On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
><libstdc++@gcc.gnu.org> wrote:
>> I propose that we deprecate the constructors for C++11/14/17/20 in
>> stage 1, and do not support them at all in C++23 mode once P1951 is
>> supported. I have a patch which I'll send in stage 1 (it also uses
>> C++20 concepts to simplify std::pair and fix PR 97930).
>>
>> After a period of deprecation we could remove them, and support P1951
>> for -std=gnu++11/14/17/20 too so that {} continues to work.
>
>The proposal sounds good to me.
Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 12:46 ` Jonathan Wakely
@ 2021-04-07 16:59 ` Jonathan Wakely
2021-04-07 17:18 ` Jonathan Wakely
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 16:59 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++
[-- Attachment #1: Type: text/plain, Size: 1815 bytes --]
On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>><libstdc++@gcc.gnu.org> wrote:
>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>
>>>After a period of deprecation we could remove them, and support P1951
>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>
>>The proposal sounds good to me.
>
>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
Here's a patch to implement it, for stage 1.
libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
This deprecates the non-standard std::pair constructors that support
construction from an rvalue and a literal zero used as a null pointer
constant. We can't just add the deprecated attribute to those
constructors, because they're currently used by correct code when they
are a better match than the constructors required by the standard e.g.
int i = 0;
const int j = 0;
std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
This patch adjusts the parameter types and constraints of those
constructors so that they only get used for literal zeros, and the
pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
for initializations that should be ill-formed we can add the deprecated
attribute.
The deprecated attribute is used to suggest that the user code uses
nullptr, which avoids the problem of 0 deducing as int instead of a null
pointer constant.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 14435 bytes --]
commit e20794c814c5961f0f33381a8eb3dff4fc741b5a
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Apr 7 17:20:43 2021
libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
This deprecates the non-standard std::pair constructors that support
construction from an rvalue and a literal zero used as a null pointer
constant. We can't just add the deprecated attribute to those
constructors, because they're currently used by correct code when they
are a better match than the constructors required by the standard e.g.
int i = 0;
const int j = 0;
std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
This patch adjusts the parameter types and constraints of those
constructors so that they only get used for literal zeros, and the
pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
for initializations that should be ill-formed we can add the deprecated
attribute.
The deprecated attribute is used to suggest that the user code uses
nullptr, which avoids the problem of 0 deducing as int instead of a null
pointer constant.
libstdc++-v3/ChangeLog:
PR libstdc++/99957
* include/bits/stl_pair.h (_PCC::_MoveCopyPair, _PCC::_CopyMovePair):
Combine and replace with ...
(_PCC::_DeprConsPair): New SFINAE helper function.
(pair): Merge preprocessor blocks so that all C++03 members
are defined together at the end.
(pair::pair(const _T1&, _U2&&), pair::pair(_U1&&, const _T2&)):
Replace _T1 and _T2 parameters with __null_ptr_constant and
adjust constraints.
* testsuite/20_util/pair/40925.cc: Use nullptr instead of 0.
* testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
* testsuite/20_util/pair/cons/99957.cc: New test.
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 70262f9508f..883d7441b3d 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
is_convertible<_U2&&, _T2>>::value;
}
- template <bool __implicit, typename _U1, typename _U2>
- static constexpr bool _CopyMovePair()
- {
- using __do_converts = __and_<is_convertible<const _U1&, _T1>,
- is_convertible<_U2&&, _T2>>;
- using __converts = typename conditional<__implicit,
- __do_converts,
- __not_<__do_converts>>::type;
- return __and_<is_constructible<_T1, const _U1&>,
- is_constructible<_T2, _U2&&>,
- __converts
- >::value;
- }
template <bool __implicit, typename _U1, typename _U2>
- static constexpr bool _MoveCopyPair()
+ static constexpr bool _DeprConsPair()
{
using __do_converts = __and_<is_convertible<_U1&&, _T1>,
- is_convertible<const _U2&, _T2>>;
+ is_convertible<_U2&&, _T2>>;
using __converts = typename conditional<__implicit,
- __do_converts,
- __not_<__do_converts>>::type;
+ __do_converts,
+ __not_<__do_converts>>::type;
return __and_<is_constructible<_T1, _U1&&>,
- is_constructible<_T2, const _U2&&>,
+ is_constructible<_T2, _U2&&>,
__converts
- >::value;
+ >::value;
}
- };
+ };
template <typename _T1, typename _T2>
struct _PCC<false, _T1, _T2>
@@ -183,7 +170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
return false;
}
- };
+ };
#endif // C++11
template<typename _U1, typename _U2> class __pair_base
@@ -217,22 +204,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_T1 first; ///< The first member
_T2 second; ///< The second member
- // _GLIBCXX_RESOLVE_LIB_DEFECTS
- // 265. std::pair::pair() effects overly restrictive
+#if __cplusplus >= 201103L
+ // C++11 (and later) implementation.
+
/** The default constructor creates @c first and @c second using their
* respective default constructors. */
-#if __cplusplus >= 201103L
template <typename _U1 = _T1,
typename _U2 = _T2,
typename enable_if<__and_<
__is_implicitly_default_constructible<_U1>,
__is_implicitly_default_constructible<_U2>>
::value, bool>::type = true>
-#endif
- _GLIBCXX_CONSTEXPR pair()
+ constexpr pair()
: first(), second() { }
-#if __cplusplus >= 201103L
template <typename _U1 = _T1,
typename _U2 = _T2,
typename enable_if<__and_<
@@ -244,13 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
::value, bool>::type = false>
explicit constexpr pair()
: first(), second() { }
-#endif
-#if __cplusplus < 201103L
- /// Two objects may be passed to a @c pair constructor to be copied.
- pair(const _T1& __a, const _T2& __b)
- : first(__a), second(__b) { }
-#else
// Shortcut for constraining the templates that don't take pairs.
/// @cond undocumented
using _PCCP = _PCC<true, _T1, _T2>;
@@ -275,14 +254,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool>::type=false>
explicit constexpr pair(const _T1& __a, const _T2& __b)
: first(__a), second(__b) { }
-#endif
-#if __cplusplus < 201103L
- /// There is also a templated constructor to convert from other pairs.
- template<typename _U1, typename _U2>
- pair(const pair<_U1, _U2>& __p)
- : first(__p.first), second(__p.second) { }
-#else
// Shortcut for constraining the templates that take pairs.
/// @cond undocumented
template <typename _U1, typename _U2>
@@ -308,40 +280,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool>::type=false>
explicit constexpr pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
-#endif
-#if __cplusplus >= 201103L
constexpr pair(const pair&) = default; ///< Copy constructor
constexpr pair(pair&&) = default; ///< Move constructor
- // DR 811.
- template<typename _U1, typename
- enable_if<_PCCP::template
- _MoveCopyPair<true, _U1, _T2>(),
- bool>::type=true>
- constexpr pair(_U1&& __x, const _T2& __y)
- : first(std::forward<_U1>(__x)), second(__y) { }
+#if _GLIBCXX_USE_DEPRECATED
+ private:
+ /// @cond undocumented
- template<typename _U1, typename
- enable_if<_PCCP::template
- _MoveCopyPair<false, _U1, _T2>(),
- bool>::type=false>
- explicit constexpr pair(_U1&& __x, const _T2& __y)
- : first(std::forward<_U1>(__x)), second(__y) { }
+ // A type which can be constructed from literal zero, but not nullptr
+ struct __null_ptr_constant
+ {
+ __null_ptr_constant(int __null_ptr_constant::*) { }
+ template<typename _Tp,
+ typename = __enable_if_t<is_null_pointer<_Tp>::value>>
+ __null_ptr_constant(_Tp) = delete;
+ };
- template<typename _U2, typename
- enable_if<_PCCP::template
- _CopyMovePair<true, _T1, _U2>(),
- bool>::type=true>
- constexpr pair(const _T1& __x, _U2&& __y)
- : first(__x), second(std::forward<_U2>(__y)) { }
+ // True if type _Up is one of _Tp& or const _Tp&
+ template<typename _Up, typename _Tp>
+ using __is_lvalue_of
+ = __or_<is_same<_Up, const _Tp&>, is_same<_Up, _Tp&>>;
- template<typename _U2, typename
- enable_if<_PCCP::template
- _CopyMovePair<false, _T1, _U2>(),
- bool>::type=false>
- explicit pair(const _T1& __x, _U2&& __y)
- : first(__x), second(std::forward<_U2>(__y)) { }
+ /// @endcond
+ public:
+
+ // Deprecated extensions to DR 811.
+ template<typename _U1,
+ __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
+ && _PCCP::template
+ _DeprConsPair<true, _U1, nullptr_t>(),
+ bool> = true>
+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+ constexpr pair(_U1&& __x, __null_ptr_constant)
+ : first(std::forward<_U1>(__x)), second(nullptr) { }
+
+ template<typename _U1,
+ __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
+ && _PCCP::template
+ _DeprConsPair<false, _U1, nullptr_t>(),
+ bool> = false>
+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+ explicit constexpr pair(_U1&& __x, __null_ptr_constant)
+ : first(std::forward<_U1>(__x)), second(nullptr) { }
+
+ template<typename _U2,
+ __enable_if_t<!__is_lvalue_of<_U2, _T2>::value
+ && _PCCP::template
+ _DeprConsPair<true, nullptr_t, _U2>(),
+ bool> = true>
+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+ constexpr pair(__null_ptr_constant, _U2&& __y)
+ : first(nullptr), second(std::forward<_U2>(__y)) { }
+
+ template<typename _U2,
+ __enable_if_t<!__is_lvalue_of<_U2, _T2>::value
+ && _PCCP::template
+ _DeprConsPair<false, nullptr_t, _U2>(),
+ bool> = false>
+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
+ explicit pair(__null_ptr_constant, _U2&& __y)
+ : first(nullptr), second(std::forward<_U2>(__y)) { }
+#endif // _GLIBCXX_USE_DEPRECATED
template<typename _U1, typename _U2, typename
enable_if<_PCCP::template
@@ -451,6 +451,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX20_CONSTEXPR
pair(tuple<_Args1...>&, tuple<_Args2...>&,
_Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+#else
+ // C++03 implementation
+
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 265. std::pair::pair() effects overly restrictive
+ /** The default constructor creates @c first and @c second using their
+ * respective default constructors. */
+ pair() : first(), second() { }
+
+ /// Two objects may be passed to a `pair` constructor to be copied.
+ pair(const _T1& __a, const _T2& __b)
+ : first(__a), second(__b) { }
+
+ /// Templated constructor to convert from other pairs.
+ template<typename _U1, typename _U2>
+ pair(const pair<_U1, _U2>& __p)
+ : first(__p.first), second(__p.second) { }
#endif // C++11
};
diff --git a/libstdc++-v3/testsuite/20_util/pair/40925.cc b/libstdc++-v3/testsuite/20_util/pair/40925.cc
index 157bef621be..c95cb380b18 100644
--- a/libstdc++-v3/testsuite/20_util/pair/40925.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/40925.cc
@@ -20,7 +20,7 @@
#include <utility>
struct X
-{
+{
explicit X(int, int) { }
private:
@@ -36,7 +36,7 @@ private:
move_only(const move_only&) = delete;
};
-// libstdc++/40925
+// libstdc++/40925 and LWG 811
void test01()
{
int *ip = 0;
@@ -52,10 +52,12 @@ void test01()
std::pair<int X::*, int X::*> p7(0, mp);
std::pair<int X::*, int X::*> p8(mp, mp);
- std::pair<int*, move_only> p9(0, move_only());
- std::pair<int X::*, move_only> p10(0, move_only());
- std::pair<move_only, int*> p11(move_only(), 0);
- std::pair<move_only, int X::*> p12(move_only(), 0);
+ // LWG 811 resolution doesn't support move-only types,
+ // so we have to use nullptr here not a literal 0.
+ std::pair<int*, move_only> p9(nullptr, move_only());
+ std::pair<int X::*, move_only> p10(nullptr, move_only());
+ std::pair<move_only, int*> p11(move_only(), nullptr);
+ std::pair<move_only, int X::*> p12(move_only(), nullptr);
std::pair<int*, move_only> p13(ip, move_only());
std::pair<int X::*, move_only> p14(mp, move_only());
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
new file mode 100644
index 00000000000..b3114131a9d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-Wdeprecated" }
+// { dg-do compile { target { c++11 } } }
+
+#include <utility>
+
+using std::pair;
+
+struct MoveOnly
+{
+ MoveOnly() = default;
+ MoveOnly(MoveOnly&&) {}
+};
+
+struct ExplicitMoveOnly
+{
+ ExplicitMoveOnly() = default;
+ ExplicitMoveOnly(ExplicitMoveOnly&&) {}
+ explicit ExplicitMoveOnly(MoveOnly&&) {}
+};
+
+// PR libstdc++/99957
+// check non-standard constructors are deprecated
+
+pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}}; // { dg-warning "deprecated" }
+pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0}; // { dg-warning "deprecated" }
+
+pair<int*, MoveOnly> v16 = {0, MoveOnly{}}; // { dg-warning "deprecated" }
+pair<MoveOnly, int*> v17 = {MoveOnly{}, 0}; // { dg-warning "deprecated" }
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
index 3d75e6dbb91..508ca32ecb7 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
@@ -126,10 +126,10 @@ struct ExplicitMoveOnly
explicit ExplicitMoveOnly(MoveOnly&&) {}
};
-std::pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}};
-std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0};
+std::pair<int*, ExplicitMoveOnly> v14{nullptr, MoveOnly{}};
+std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, nullptr};
std::pair<int*, ExplicitMoveOnly> v16 =
- {0, MoveOnly{}}; // { dg-error "could not convert" }
+ {nullptr, MoveOnly{}}; // { dg-error "could not convert" }
std::pair<ExplicitMoveOnly, int*> v17 =
- {MoveOnly{}, 0}; // { dg-error "could not convert" }
+ {MoveOnly{}, nullptr}; // { dg-error "could not convert" }
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 16:59 ` Jonathan Wakely
@ 2021-04-07 17:18 ` Jonathan Wakely
2021-04-28 16:57 ` Jonathan Wakely
2021-04-07 18:00 ` Jonathan Wakely
2021-04-28 16:57 ` Jonathan Wakely
2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 17:18 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++
[-- Attachment #1: Type: text/plain, Size: 947 bytes --]
On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.
And here's a patch to simplify the std::pair constraints using
concepts, also for consideration in stage 1.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 11226 bytes --]
commit 9e184c70f6a8e8ed8e34d8ffcb9c98e079dd3c31
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Apr 7 18:12:08 2021
libstdc++: Simplify std::pair constraints using concepts
This re-implements the constraints on the std::pair constructors and
assignment operators in C++20 mode, to use concepts.
The non-standard constructors deprecated for PR 99957 are no longer
supported in C++20 mode, which requires some minor testsuite changes.
Otherwise all tests pass in C++20 mode.
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (pair):
* testsuite/20_util/pair/cons/99957.cc: Disable for C++20 and
later.
* testsuite/20_util/pair/cons/explicit_construct.cc: Adjust
expected error messages to also match C++20 errors.
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 883d7441b3d..bd3f911abb9 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -92,6 +92,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<size_t...>
struct _Index_tuple;
+#if ! __cpp_lib_concepts
// Concept utility functions, reused in conditionally-explicit
// constructors.
// See PR 70437, don't look at is_constructible or
@@ -171,11 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return false;
}
};
+#endif // lib concepts
#endif // C++11
template<typename _U1, typename _U2> class __pair_base
{
-#if __cplusplus >= 201103L
+#if __cplusplus >= 201103L && ! __cpp_lib_concepts
template<typename _T1, typename _T2> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
@@ -196,7 +198,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
template<typename _T1, typename _T2>
struct pair
- : private __pair_base<_T1, _T2>
+ : public __pair_base<_T1, _T2>
{
typedef _T1 first_type; ///< The type of the `first` member
typedef _T2 second_type; ///< The type of the `second` member
@@ -205,7 +207,186 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_T2 second; ///< The second member
#if __cplusplus >= 201103L
- // C++11 (and later) implementation.
+ constexpr pair(const pair&) = default; ///< Copy constructor
+ constexpr pair(pair&&) = default; ///< Move constructor
+
+ template<typename... _Args1, typename... _Args2>
+ _GLIBCXX20_CONSTEXPR
+ pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
+
+ /// Swap the first members and then the second members.
+ _GLIBCXX20_CONSTEXPR void
+ swap(pair& __p)
+ noexcept(__and_<__is_nothrow_swappable<_T1>,
+ __is_nothrow_swappable<_T2>>::value)
+ {
+ using std::swap;
+ swap(first, __p.first);
+ swap(second, __p.second);
+ }
+
+ private:
+ template<typename... _Args1, size_t... _Indexes1,
+ typename... _Args2, size_t... _Indexes2>
+ _GLIBCXX20_CONSTEXPR
+ pair(tuple<_Args1...>&, tuple<_Args2...>&,
+ _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+ public:
+
+#if __cpp_lib_concepts
+ // C++20 implementation using concepts, explicit(bool), fully constexpr.
+
+ /// Default constructor
+ constexpr
+ explicit(__not_<__and_<__is_implicitly_default_constructible<_T1>,
+ __is_implicitly_default_constructible<_T2>>>())
+ pair()
+ requires is_default_constructible_v<_T1>
+ && is_default_constructible_v<_T2>
+ : first(), second()
+ { }
+
+ private:
+
+ /// @cond undocumented
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_constructible()
+ {
+ if constexpr (is_constructible_v<_T1, _U1>)
+ return is_constructible_v<_T2, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_nothrow_constructible()
+ {
+ if constexpr (is_nothrow_constructible_v<_T1, _U1>)
+ return is_nothrow_constructible_v<_T2, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_convertible()
+ {
+ if constexpr (is_convertible_v<_U1, _T1>)
+ return is_convertible_v<_U2, _T2>;
+ return false;
+ }
+ /// @endcond
+
+ public:
+
+ /// Constructor accepting lvalues of `first_type` and `second_type`
+ constexpr explicit(!_S_convertible<const _T1&, const _T2&>())
+ pair(const _T1& __x, const _T2& __y)
+ noexcept(_S_nothrow_constructible<const _T1&, const _T2&>())
+ requires (_S_constructible<const _T1&, const _T2&>())
+ : first(__x), second(__y)
+ { }
+
+ /// Constructor accepting two values of arbitrary types
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<_U1, _U2>())
+ constexpr explicit(!_S_convertible<_U1, _U2>())
+ pair(_U1&& __x, _U2&& __y)
+ noexcept(_S_nothrow_constructible<_U1, _U2>())
+ : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
+ { }
+
+ /// Converting constructor from a `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<const _U1&, const _U2&>())
+ constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
+ pair(const pair<_U1, _U2>& __p)
+ noexcept(_S_nothrow_constructible<const _U1&, const _U2&>())
+ : first(__p.first), second(__p.second)
+ { }
+
+ /// Converting constructor from a `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<_U1, _U2>())
+ constexpr explicit(!_S_convertible<_U1, _U2>())
+ pair(pair<_U1, _U2>&& __p)
+ noexcept(_S_nothrow_constructible<_U1, _U2>())
+ : first(std::forward<_U1>(__p.first)),
+ second(std::forward<_U2>(__p.second))
+ { }
+
+ private:
+ /// @cond undocumented
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_assignable()
+ {
+ if constexpr (is_assignable_v<_T1&, _U1>)
+ return is_assignable_v<_T2&, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_nothrow_assignable()
+ {
+ if constexpr (is_nothrow_assignable_v<_T1&, _U1>)
+ return is_nothrow_assignable_v<_T2&, _U2>;
+ return false;
+ }
+ /// @endcond
+
+ public:
+
+ pair& operator=(const pair&) = delete;
+
+ /// Copy assignment operator
+ constexpr pair&
+ operator=(const pair& __p)
+ noexcept(_S_nothrow_assignable<const _T1&, const _T2&>())
+ requires (_S_assignable<const _T1&, const _T2&>())
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Move assignment operator
+ constexpr pair&
+ operator=(pair&& __p)
+ noexcept(_S_nothrow_assignable<_T1, _T2>())
+ requires (_S_assignable<_T1, _T2>())
+ {
+ first = std::forward<first_type>(__p.first);
+ second = std::forward<second_type>(__p.second);
+ return *this;
+ }
+
+ /// Converting assignment from a `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ constexpr pair&
+ operator=(const pair<_U1, _U2>& __p)
+ noexcept(_S_nothrow_assignable<const _U1&, const _U2&>())
+ requires (_S_assignable<const _U1&, const _U2&>())
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Converting assignment from a `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ constexpr pair&
+ operator=(pair<_U1, _U2>&& __p)
+ noexcept(_S_nothrow_assignable<_U1, _U2>())
+ requires (_S_assignable<_U1, _U2>())
+ {
+ first = std::forward<_U1>(__p.first);
+ second = std::forward<_U2>(__p.second);
+ return *this;
+ }
+#else
+ // C++11/14/17 implementation using enable_if, partially constexpr.
/** The default constructor creates @c first and @c second using their
* respective default constructors. */
@@ -281,9 +462,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
explicit constexpr pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
- constexpr pair(const pair&) = default; ///< Copy constructor
- constexpr pair(pair&&) = default; ///< Move constructor
-
#if _GLIBCXX_USE_DEPRECATED
private:
/// @cond undocumented
@@ -341,7 +519,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
explicit pair(__null_ptr_constant, _U2&& __y)
: first(nullptr), second(std::forward<_U2>(__y)) { }
-#endif // _GLIBCXX_USE_DEPRECATED
+#endif
template<typename _U1, typename _U2, typename
enable_if<_PCCP::template
@@ -382,10 +560,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(std::forward<_U1>(__p.first)),
second(std::forward<_U2>(__p.second)) { }
- template<typename... _Args1, typename... _Args2>
- _GLIBCXX20_CONSTEXPR
- pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
-
_GLIBCXX20_CONSTEXPR pair&
operator=(typename conditional<
__and_<is_copy_assignable<_T1>,
@@ -433,24 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second = std::forward<_U2>(__p.second);
return *this;
}
-
- /// Swap the first members and then the second members.
- _GLIBCXX20_CONSTEXPR void
- swap(pair& __p)
- noexcept(__and_<__is_nothrow_swappable<_T1>,
- __is_nothrow_swappable<_T2>>::value)
- {
- using std::swap;
- swap(first, __p.first);
- swap(second, __p.second);
- }
-
- private:
- template<typename... _Args1, size_t... _Indexes1,
- typename... _Args2, size_t... _Indexes2>
- _GLIBCXX20_CONSTEXPR
- pair(tuple<_Args1...>&, tuple<_Args2...>&,
- _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+#endif // lib concepts
#else
// C++03 implementation
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
index d300292b463..a3e6a452583 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
@@ -16,7 +16,7 @@
// <http://www.gnu.org/licenses/>.
// { dg-options "-Wdeprecated" }
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && { ! c++20 } } } }
#include <utility>
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
index 508ca32ecb7..ecd5acf9375 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
@@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {}
std::pair<ExplicitDefault, int> f8()
{
- return {}; // { dg-error "could not convert" }
+ return {}; // { dg-error "convert" }
}
std::pair<ExplicitDefaultDefault, int> f9()
{
- return {}; // { dg-error "could not convert" }
+ return {}; // { dg-error "convert" }
}
void f10(std::pair<ExplicitDefault, int>) {}
@@ -107,8 +107,8 @@ void test_arg_passing()
f7({1,2});
f7(std::pair<int, int>{});
f7(std::pair<long, long>{});
- f10({}); // { dg-error "could not convert" }
- f11({}); // { dg-error "could not convert" }
+ f10({}); // { dg-error "convert" }
+ f11({}); // { dg-error "convert" }
f10(std::pair<ExplicitDefault, int>{});
f11(std::pair<ExplicitDefaultDefault, int>{});
}
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 16:59 ` Jonathan Wakely
2021-04-07 17:18 ` Jonathan Wakely
@ 2021-04-07 18:00 ` Jonathan Wakely
2021-04-07 18:17 ` Jonathan Wakely
2021-04-28 16:57 ` Jonathan Wakely
2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 18:00 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++
On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.
>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 70262f9508f..883d7441b3d 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> is_convertible<_U2&&, _T2>>::value;
> }
>
>- template <bool __implicit, typename _U1, typename _U2>
>- static constexpr bool _CopyMovePair()
>- {
>- using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>- is_convertible<_U2&&, _T2>>;
>- using __converts = typename conditional<__implicit,
>- __do_converts,
>- __not_<__do_converts>>::type;
>- return __and_<is_constructible<_T1, const _U1&>,
>- is_constructible<_T2, _U2&&>,
>- __converts
>- >::value;
>- }
>
> template <bool __implicit, typename _U1, typename _U2>
>- static constexpr bool _MoveCopyPair()
>+ static constexpr bool _DeprConsPair()
> {
> using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>- is_convertible<const _U2&, _T2>>;
>+ is_convertible<_U2&&, _T2>>;
> using __converts = typename conditional<__implicit,
>- __do_converts,
>- __not_<__do_converts>>::type;
>+ __do_converts,
>+ __not_<__do_converts>>::type;
> return __and_<is_constructible<_T1, _U1&&>,
>- is_constructible<_T2, const _U2&&>,
>+ is_constructible<_T2, _U2&&>,
N.B. this fixes a bug in the line above, where const _U2&& is used in
place of const _U2&.
I'll create a testcase that tickles the bug and report it to bugzilla
tomorrow.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 18:00 ` Jonathan Wakely
@ 2021-04-07 18:17 ` Jonathan Wakely
2021-04-07 18:25 ` Ville Voutilainen
0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-07 18:17 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++
On 07/04/21 19:00 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>
>
>>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>>index 70262f9508f..883d7441b3d 100644
>>--- a/libstdc++-v3/include/bits/stl_pair.h
>>+++ b/libstdc++-v3/include/bits/stl_pair.h
>>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> is_convertible<_U2&&, _T2>>::value;
>> }
>>
>>- template <bool __implicit, typename _U1, typename _U2>
>>- static constexpr bool _CopyMovePair()
>>- {
>>- using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>>- is_convertible<_U2&&, _T2>>;
>>- using __converts = typename conditional<__implicit,
>>- __do_converts,
>>- __not_<__do_converts>>::type;
>>- return __and_<is_constructible<_T1, const _U1&>,
>>- is_constructible<_T2, _U2&&>,
>>- __converts
>>- >::value;
>>- }
>>
>> template <bool __implicit, typename _U1, typename _U2>
>>- static constexpr bool _MoveCopyPair()
>>+ static constexpr bool _DeprConsPair()
>> {
>> using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>>- is_convertible<const _U2&, _T2>>;
>>+ is_convertible<_U2&&, _T2>>;
>> using __converts = typename conditional<__implicit,
>>- __do_converts,
>>- __not_<__do_converts>>::type;
>>+ __do_converts,
>>+ __not_<__do_converts>>::type;
>> return __and_<is_constructible<_T1, _U1&&>,
>>- is_constructible<_T2, const _U2&&>,
>>+ is_constructible<_T2, _U2&&>,
>
>N.B. this fixes a bug in the line above, where const _U2&& is used in
>place of const _U2&.
>
>I'll create a testcase that tickles the bug and report it to bugzilla
>tomorrow.
This fails to compile because of that bug:
#include <utility>
struct X {
X(void* = 0) { }
X(const X&) = default;
X(const X&&) = delete;
};
struct move_only {
move_only() = default;
move_only(move_only&&) = default;
};
std::pair<move_only, X> p0(move_only(), 0);
std::pair<move_only, X> p1(move_only(), {});
The pair(U1&&, const T2&) constructor should be viable, but it fails
the _MoveCopyPair constraint check because X(const X&&) is deleted.
I'm not sure I care about this though. It would only work because of
those non-standard constructors which we're talking about deprecating.
I'm not very motivated to fix them so they accept this, when we're
going to deprecate them anyway.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 18:17 ` Jonathan Wakely
@ 2021-04-07 18:25 ` Ville Voutilainen
2021-04-07 18:26 ` Ville Voutilainen
0 siblings, 1 reply; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 18:25 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: libstdc++
On Wed, 7 Apr 2021 at 21:17, Jonathan Wakely <jwakely@redhat.com> wrote:
> This fails to compile because of that bug:
>
> #include <utility>
>
> struct X {
> X(void* = 0) { }
> X(const X&) = default;
> X(const X&&) = delete;
> };
>
> struct move_only {
> move_only() = default;
> move_only(move_only&&) = default;
> };
>
> std::pair<move_only, X> p0(move_only(), 0);
> std::pair<move_only, X> p1(move_only(), {});
>
> The pair(U1&&, const T2&) constructor should be viable, but it fails
> the _MoveCopyPair constraint check because X(const X&&) is deleted.
>
> I'm not sure I care about this though. It would only work because of
> those non-standard constructors which we're talking about deprecating.
> I'm not very motivated to fix them so they accept this, when we're
> going to deprecate them anyway.
Nah, let's not bother. The type X has an explicitly deleted move
constructor, which the library
doesn't go to heroic extents to support.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 18:25 ` Ville Voutilainen
@ 2021-04-07 18:26 ` Ville Voutilainen
0 siblings, 0 replies; 14+ messages in thread
From: Ville Voutilainen @ 2021-04-07 18:26 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: libstdc++
On Wed, 7 Apr 2021 at 21:25, Ville Voutilainen
<ville.voutilainen@gmail.com> wrote:
>
> On Wed, 7 Apr 2021 at 21:17, Jonathan Wakely <jwakely@redhat.com> wrote:
> > This fails to compile because of that bug:
> >
> > #include <utility>
> >
> > struct X {
> > X(void* = 0) { }
> > X(const X&) = default;
> > X(const X&&) = delete;
> > };
> >
> > struct move_only {
> > move_only() = default;
> > move_only(move_only&&) = default;
> > };
> >
> > std::pair<move_only, X> p0(move_only(), 0);
> > std::pair<move_only, X> p1(move_only(), {});
> >
> > The pair(U1&&, const T2&) constructor should be viable, but it fails
> > the _MoveCopyPair constraint check because X(const X&&) is deleted.
> >
> > I'm not sure I care about this though. It would only work because of
> > those non-standard constructors which we're talking about deprecating.
> > I'm not very motivated to fix them so they accept this, when we're
> > going to deprecate them anyway.
>
> Nah, let's not bother. The type X has an explicitly deleted move
> constructor, which the library
> doesn't go to heroic extents to support.
And more than that, it's trying to be a "copy-only" type, which the
library doesn't try to support
at all, quite the contrary.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 16:59 ` Jonathan Wakely
2021-04-07 17:18 ` Jonathan Wakely
2021-04-07 18:00 ` Jonathan Wakely
@ 2021-04-28 16:57 ` Jonathan Wakely
2021-04-28 17:00 ` Jonathan Wakely
2 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 16:57 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++, gcc-patches
On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>><libstdc++@gcc.gnu.org> wrote:
>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>
>>>>After a period of deprecation we could remove them, and support P1951
>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>
>>>The proposal sounds good to me.
>>
>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>
>Here's a patch to implement it, for stage 1.
>
> libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>
> This deprecates the non-standard std::pair constructors that support
> construction from an rvalue and a literal zero used as a null pointer
> constant. We can't just add the deprecated attribute to those
> constructors, because they're currently used by correct code when they
> are a better match than the constructors required by the standard e.g.
>
> int i = 0;
> const int j = 0;
> std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>
> This patch adjusts the parameter types and constraints of those
> constructors so that they only get used for literal zeros, and the
> pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
> for initializations that should be ill-formed we can add the deprecated
> attribute.
>
> The deprecated attribute is used to suggest that the user code uses
> nullptr, which avoids the problem of 0 deducing as int instead of a null
> pointer constant.
I've pushed this to trunk, after testing on powerpc64le-linux.
>commit e20794c814c5961f0f33381a8eb3dff4fc741b5a
>Author: Jonathan Wakely <jwakely@redhat.com>
>Date: Wed Apr 7 17:20:43 2021
>
> libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>
> This deprecates the non-standard std::pair constructors that support
> construction from an rvalue and a literal zero used as a null pointer
> constant. We can't just add the deprecated attribute to those
> constructors, because they're currently used by correct code when they
> are a better match than the constructors required by the standard e.g.
>
> int i = 0;
> const int j = 0;
> std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>
> This patch adjusts the parameter types and constraints of those
> constructors so that they only get used for literal zeros, and the
> pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
> for initializations that should be ill-formed we can add the deprecated
> attribute.
>
> The deprecated attribute is used to suggest that the user code uses
> nullptr, which avoids the problem of 0 deducing as int instead of a null
> pointer constant.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/99957
> * include/bits/stl_pair.h (_PCC::_MoveCopyPair, _PCC::_CopyMovePair):
> Combine and replace with ...
> (_PCC::_DeprConsPair): New SFINAE helper function.
> (pair): Merge preprocessor blocks so that all C++03 members
> are defined together at the end.
> (pair::pair(const _T1&, _U2&&), pair::pair(_U1&&, const _T2&)):
> Replace _T1 and _T2 parameters with __null_ptr_constant and
> adjust constraints.
> * testsuite/20_util/pair/40925.cc: Use nullptr instead of 0.
> * testsuite/20_util/pair/cons/explicit_construct.cc: Likewise.
> * testsuite/20_util/pair/cons/99957.cc: New test.
>
>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 70262f9508f..883d7441b3d 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -128,34 +128,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> is_convertible<_U2&&, _T2>>::value;
> }
>
>- template <bool __implicit, typename _U1, typename _U2>
>- static constexpr bool _CopyMovePair()
>- {
>- using __do_converts = __and_<is_convertible<const _U1&, _T1>,
>- is_convertible<_U2&&, _T2>>;
>- using __converts = typename conditional<__implicit,
>- __do_converts,
>- __not_<__do_converts>>::type;
>- return __and_<is_constructible<_T1, const _U1&>,
>- is_constructible<_T2, _U2&&>,
>- __converts
>- >::value;
>- }
>
> template <bool __implicit, typename _U1, typename _U2>
>- static constexpr bool _MoveCopyPair()
>+ static constexpr bool _DeprConsPair()
> {
> using __do_converts = __and_<is_convertible<_U1&&, _T1>,
>- is_convertible<const _U2&, _T2>>;
>+ is_convertible<_U2&&, _T2>>;
> using __converts = typename conditional<__implicit,
>- __do_converts,
>- __not_<__do_converts>>::type;
>+ __do_converts,
>+ __not_<__do_converts>>::type;
> return __and_<is_constructible<_T1, _U1&&>,
>- is_constructible<_T2, const _U2&&>,
>+ is_constructible<_T2, _U2&&>,
> __converts
>- >::value;
>+ >::value;
> }
>- };
>+ };
>
> template <typename _T1, typename _T2>
> struct _PCC<false, _T1, _T2>
>@@ -183,7 +170,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> {
> return false;
> }
>- };
>+ };
> #endif // C++11
>
> template<typename _U1, typename _U2> class __pair_base
>@@ -217,22 +204,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _T1 first; ///< The first member
> _T2 second; ///< The second member
>
>- // _GLIBCXX_RESOLVE_LIB_DEFECTS
>- // 265. std::pair::pair() effects overly restrictive
>+#if __cplusplus >= 201103L
>+ // C++11 (and later) implementation.
>+
> /** The default constructor creates @c first and @c second using their
> * respective default constructors. */
>-#if __cplusplus >= 201103L
> template <typename _U1 = _T1,
> typename _U2 = _T2,
> typename enable_if<__and_<
> __is_implicitly_default_constructible<_U1>,
> __is_implicitly_default_constructible<_U2>>
> ::value, bool>::type = true>
>-#endif
>- _GLIBCXX_CONSTEXPR pair()
>+ constexpr pair()
> : first(), second() { }
>
>-#if __cplusplus >= 201103L
> template <typename _U1 = _T1,
> typename _U2 = _T2,
> typename enable_if<__and_<
>@@ -244,13 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> ::value, bool>::type = false>
> explicit constexpr pair()
> : first(), second() { }
>-#endif
>
>-#if __cplusplus < 201103L
>- /// Two objects may be passed to a @c pair constructor to be copied.
>- pair(const _T1& __a, const _T2& __b)
>- : first(__a), second(__b) { }
>-#else
> // Shortcut for constraining the templates that don't take pairs.
> /// @cond undocumented
> using _PCCP = _PCC<true, _T1, _T2>;
>@@ -275,14 +254,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> bool>::type=false>
> explicit constexpr pair(const _T1& __a, const _T2& __b)
> : first(__a), second(__b) { }
>-#endif
>
>-#if __cplusplus < 201103L
>- /// There is also a templated constructor to convert from other pairs.
>- template<typename _U1, typename _U2>
>- pair(const pair<_U1, _U2>& __p)
>- : first(__p.first), second(__p.second) { }
>-#else
> // Shortcut for constraining the templates that take pairs.
> /// @cond undocumented
> template <typename _U1, typename _U2>
>@@ -308,40 +280,68 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> bool>::type=false>
> explicit constexpr pair(const pair<_U1, _U2>& __p)
> : first(__p.first), second(__p.second) { }
>-#endif
>
>-#if __cplusplus >= 201103L
> constexpr pair(const pair&) = default; ///< Copy constructor
> constexpr pair(pair&&) = default; ///< Move constructor
>
>- // DR 811.
>- template<typename _U1, typename
>- enable_if<_PCCP::template
>- _MoveCopyPair<true, _U1, _T2>(),
>- bool>::type=true>
>- constexpr pair(_U1&& __x, const _T2& __y)
>- : first(std::forward<_U1>(__x)), second(__y) { }
>+#if _GLIBCXX_USE_DEPRECATED
>+ private:
>+ /// @cond undocumented
>
>- template<typename _U1, typename
>- enable_if<_PCCP::template
>- _MoveCopyPair<false, _U1, _T2>(),
>- bool>::type=false>
>- explicit constexpr pair(_U1&& __x, const _T2& __y)
>- : first(std::forward<_U1>(__x)), second(__y) { }
>+ // A type which can be constructed from literal zero, but not nullptr
>+ struct __null_ptr_constant
>+ {
>+ __null_ptr_constant(int __null_ptr_constant::*) { }
>+ template<typename _Tp,
>+ typename = __enable_if_t<is_null_pointer<_Tp>::value>>
>+ __null_ptr_constant(_Tp) = delete;
>+ };
>
>- template<typename _U2, typename
>- enable_if<_PCCP::template
>- _CopyMovePair<true, _T1, _U2>(),
>- bool>::type=true>
>- constexpr pair(const _T1& __x, _U2&& __y)
>- : first(__x), second(std::forward<_U2>(__y)) { }
>+ // True if type _Up is one of _Tp& or const _Tp&
>+ template<typename _Up, typename _Tp>
>+ using __is_lvalue_of
>+ = __or_<is_same<_Up, const _Tp&>, is_same<_Up, _Tp&>>;
>
>- template<typename _U2, typename
>- enable_if<_PCCP::template
>- _CopyMovePair<false, _T1, _U2>(),
>- bool>::type=false>
>- explicit pair(const _T1& __x, _U2&& __y)
>- : first(__x), second(std::forward<_U2>(__y)) { }
>+ /// @endcond
>+ public:
>+
>+ // Deprecated extensions to DR 811.
>+ template<typename _U1,
>+ __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
>+ && _PCCP::template
>+ _DeprConsPair<true, _U1, nullptr_t>(),
>+ bool> = true>
>+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+ constexpr pair(_U1&& __x, __null_ptr_constant)
>+ : first(std::forward<_U1>(__x)), second(nullptr) { }
>+
>+ template<typename _U1,
>+ __enable_if_t<!__is_lvalue_of<_U1, _T1>::value
>+ && _PCCP::template
>+ _DeprConsPair<false, _U1, nullptr_t>(),
>+ bool> = false>
>+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+ explicit constexpr pair(_U1&& __x, __null_ptr_constant)
>+ : first(std::forward<_U1>(__x)), second(nullptr) { }
>+
>+ template<typename _U2,
>+ __enable_if_t<!__is_lvalue_of<_U2, _T2>::value
>+ && _PCCP::template
>+ _DeprConsPair<true, nullptr_t, _U2>(),
>+ bool> = true>
>+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+ constexpr pair(__null_ptr_constant, _U2&& __y)
>+ : first(nullptr), second(std::forward<_U2>(__y)) { }
>+
>+ template<typename _U2,
>+ __enable_if_t<!__is_lvalue_of<_U2, _T2>::value
>+ && _PCCP::template
>+ _DeprConsPair<false, nullptr_t, _U2>(),
>+ bool> = false>
>+ _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
>+ explicit pair(__null_ptr_constant, _U2&& __y)
>+ : first(nullptr), second(std::forward<_U2>(__y)) { }
>+#endif // _GLIBCXX_USE_DEPRECATED
>
> template<typename _U1, typename _U2, typename
> enable_if<_PCCP::template
>@@ -451,6 +451,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _GLIBCXX20_CONSTEXPR
> pair(tuple<_Args1...>&, tuple<_Args2...>&,
> _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+#else
>+ // C++03 implementation
>+
>+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+ // 265. std::pair::pair() effects overly restrictive
>+ /** The default constructor creates @c first and @c second using their
>+ * respective default constructors. */
>+ pair() : first(), second() { }
>+
>+ /// Two objects may be passed to a `pair` constructor to be copied.
>+ pair(const _T1& __a, const _T2& __b)
>+ : first(__a), second(__b) { }
>+
>+ /// Templated constructor to convert from other pairs.
>+ template<typename _U1, typename _U2>
>+ pair(const pair<_U1, _U2>& __p)
>+ : first(__p.first), second(__p.second) { }
> #endif // C++11
> };
>
>diff --git a/libstdc++-v3/testsuite/20_util/pair/40925.cc b/libstdc++-v3/testsuite/20_util/pair/40925.cc
>index 157bef621be..c95cb380b18 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/40925.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/40925.cc
>@@ -20,7 +20,7 @@
> #include <utility>
>
> struct X
>-{
>+{
> explicit X(int, int) { }
>
> private:
>@@ -36,7 +36,7 @@ private:
> move_only(const move_only&) = delete;
> };
>
>-// libstdc++/40925
>+// libstdc++/40925 and LWG 811
> void test01()
> {
> int *ip = 0;
>@@ -52,10 +52,12 @@ void test01()
> std::pair<int X::*, int X::*> p7(0, mp);
> std::pair<int X::*, int X::*> p8(mp, mp);
>
>- std::pair<int*, move_only> p9(0, move_only());
>- std::pair<int X::*, move_only> p10(0, move_only());
>- std::pair<move_only, int*> p11(move_only(), 0);
>- std::pair<move_only, int X::*> p12(move_only(), 0);
>+ // LWG 811 resolution doesn't support move-only types,
>+ // so we have to use nullptr here not a literal 0.
>+ std::pair<int*, move_only> p9(nullptr, move_only());
>+ std::pair<int X::*, move_only> p10(nullptr, move_only());
>+ std::pair<move_only, int*> p11(move_only(), nullptr);
>+ std::pair<move_only, int X::*> p12(move_only(), nullptr);
>
> std::pair<int*, move_only> p13(ip, move_only());
> std::pair<int X::*, move_only> p14(mp, move_only());
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>new file mode 100644
>index 00000000000..b3114131a9d
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>@@ -0,0 +1,45 @@
>+// Copyright (C) 2021 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-Wdeprecated" }
>+// { dg-do compile { target { c++11 } } }
>+
>+#include <utility>
>+
>+using std::pair;
>+
>+struct MoveOnly
>+{
>+ MoveOnly() = default;
>+ MoveOnly(MoveOnly&&) {}
>+};
>+
>+struct ExplicitMoveOnly
>+{
>+ ExplicitMoveOnly() = default;
>+ ExplicitMoveOnly(ExplicitMoveOnly&&) {}
>+ explicit ExplicitMoveOnly(MoveOnly&&) {}
>+};
>+
>+// PR libstdc++/99957
>+// check non-standard constructors are deprecated
>+
>+pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}}; // { dg-warning "deprecated" }
>+pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0}; // { dg-warning "deprecated" }
>+
>+pair<int*, MoveOnly> v16 = {0, MoveOnly{}}; // { dg-warning "deprecated" }
>+pair<MoveOnly, int*> v17 = {MoveOnly{}, 0}; // { dg-warning "deprecated" }
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>index 3d75e6dbb91..508ca32ecb7 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>@@ -126,10 +126,10 @@ struct ExplicitMoveOnly
> explicit ExplicitMoveOnly(MoveOnly&&) {}
> };
>
>-std::pair<int*, ExplicitMoveOnly> v14{0, MoveOnly{}};
>-std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, 0};
>+std::pair<int*, ExplicitMoveOnly> v14{nullptr, MoveOnly{}};
>+std::pair<ExplicitMoveOnly, int*> v15{MoveOnly{}, nullptr};
>
> std::pair<int*, ExplicitMoveOnly> v16 =
>- {0, MoveOnly{}}; // { dg-error "could not convert" }
>+ {nullptr, MoveOnly{}}; // { dg-error "could not convert" }
> std::pair<ExplicitMoveOnly, int*> v17 =
>- {MoveOnly{}, 0}; // { dg-error "could not convert" }
>+ {MoveOnly{}, nullptr}; // { dg-error "could not convert" }
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-07 17:18 ` Jonathan Wakely
@ 2021-04-28 16:57 ` Jonathan Wakely
2021-04-28 17:19 ` Jonathan Wakely
0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 16:57 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++, gcc-patches
On 07/04/21 18:18 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>
>And here's a patch to simplify the std::pair constraints using
>concepts, also for consideration in stage 1.
I've pushed this to trunk too, after testing on powerpc64le-linux.
>commit 9e184c70f6a8e8ed8e34d8ffcb9c98e079dd3c31
>Author: Jonathan Wakely <jwakely@redhat.com>
>Date: Wed Apr 7 18:12:08 2021
>
> libstdc++: Simplify std::pair constraints using concepts
>
> This re-implements the constraints on the std::pair constructors and
> assignment operators in C++20 mode, to use concepts.
>
> The non-standard constructors deprecated for PR 99957 are no longer
> supported in C++20 mode, which requires some minor testsuite changes.
> Otherwise all tests pass in C++20 mode.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/stl_pair.h (pair):
> * testsuite/20_util/pair/cons/99957.cc: Disable for C++20 and
> later.
> * testsuite/20_util/pair/cons/explicit_construct.cc: Adjust
> expected error messages to also match C++20 errors.
>
>diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
>index 883d7441b3d..bd3f911abb9 100644
>--- a/libstdc++-v3/include/bits/stl_pair.h
>+++ b/libstdc++-v3/include/bits/stl_pair.h
>@@ -92,6 +92,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<size_t...>
> struct _Index_tuple;
>
>+#if ! __cpp_lib_concepts
> // Concept utility functions, reused in conditionally-explicit
> // constructors.
> // See PR 70437, don't look at is_constructible or
>@@ -171,11 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> return false;
> }
> };
>+#endif // lib concepts
> #endif // C++11
>
> template<typename _U1, typename _U2> class __pair_base
> {
>-#if __cplusplus >= 201103L
>+#if __cplusplus >= 201103L && ! __cpp_lib_concepts
> template<typename _T1, typename _T2> friend struct pair;
> __pair_base() = default;
> ~__pair_base() = default;
>@@ -196,7 +198,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> */
> template<typename _T1, typename _T2>
> struct pair
>- : private __pair_base<_T1, _T2>
>+ : public __pair_base<_T1, _T2>
> {
> typedef _T1 first_type; ///< The type of the `first` member
> typedef _T2 second_type; ///< The type of the `second` member
>@@ -205,7 +207,186 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _T2 second; ///< The second member
>
> #if __cplusplus >= 201103L
>- // C++11 (and later) implementation.
>+ constexpr pair(const pair&) = default; ///< Copy constructor
>+ constexpr pair(pair&&) = default; ///< Move constructor
>+
>+ template<typename... _Args1, typename... _Args2>
>+ _GLIBCXX20_CONSTEXPR
>+ pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
>+
>+ /// Swap the first members and then the second members.
>+ _GLIBCXX20_CONSTEXPR void
>+ swap(pair& __p)
>+ noexcept(__and_<__is_nothrow_swappable<_T1>,
>+ __is_nothrow_swappable<_T2>>::value)
>+ {
>+ using std::swap;
>+ swap(first, __p.first);
>+ swap(second, __p.second);
>+ }
>+
>+ private:
>+ template<typename... _Args1, size_t... _Indexes1,
>+ typename... _Args2, size_t... _Indexes2>
>+ _GLIBCXX20_CONSTEXPR
>+ pair(tuple<_Args1...>&, tuple<_Args2...>&,
>+ _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+ public:
>+
>+#if __cpp_lib_concepts
>+ // C++20 implementation using concepts, explicit(bool), fully constexpr.
>+
>+ /// Default constructor
>+ constexpr
>+ explicit(__not_<__and_<__is_implicitly_default_constructible<_T1>,
>+ __is_implicitly_default_constructible<_T2>>>())
>+ pair()
>+ requires is_default_constructible_v<_T1>
>+ && is_default_constructible_v<_T2>
>+ : first(), second()
>+ { }
>+
>+ private:
>+
>+ /// @cond undocumented
>+ template<typename _U1, typename _U2>
>+ static constexpr bool
>+ _S_constructible()
>+ {
>+ if constexpr (is_constructible_v<_T1, _U1>)
>+ return is_constructible_v<_T2, _U2>;
>+ return false;
>+ }
>+
>+ template<typename _U1, typename _U2>
>+ static constexpr bool
>+ _S_nothrow_constructible()
>+ {
>+ if constexpr (is_nothrow_constructible_v<_T1, _U1>)
>+ return is_nothrow_constructible_v<_T2, _U2>;
>+ return false;
>+ }
>+
>+ template<typename _U1, typename _U2>
>+ static constexpr bool
>+ _S_convertible()
>+ {
>+ if constexpr (is_convertible_v<_U1, _T1>)
>+ return is_convertible_v<_U2, _T2>;
>+ return false;
>+ }
>+ /// @endcond
>+
>+ public:
>+
>+ /// Constructor accepting lvalues of `first_type` and `second_type`
>+ constexpr explicit(!_S_convertible<const _T1&, const _T2&>())
>+ pair(const _T1& __x, const _T2& __y)
>+ noexcept(_S_nothrow_constructible<const _T1&, const _T2&>())
>+ requires (_S_constructible<const _T1&, const _T2&>())
>+ : first(__x), second(__y)
>+ { }
>+
>+ /// Constructor accepting two values of arbitrary types
>+ template<typename _U1, typename _U2>
>+ requires (_S_constructible<_U1, _U2>())
>+ constexpr explicit(!_S_convertible<_U1, _U2>())
>+ pair(_U1&& __x, _U2&& __y)
>+ noexcept(_S_nothrow_constructible<_U1, _U2>())
>+ : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
>+ { }
>+
>+ /// Converting constructor from a `pair<U1, U2>` lvalue
>+ template<typename _U1, typename _U2>
>+ requires (_S_constructible<const _U1&, const _U2&>())
>+ constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
>+ pair(const pair<_U1, _U2>& __p)
>+ noexcept(_S_nothrow_constructible<const _U1&, const _U2&>())
>+ : first(__p.first), second(__p.second)
>+ { }
>+
>+ /// Converting constructor from a `pair<U1, U2>` rvalue
>+ template<typename _U1, typename _U2>
>+ requires (_S_constructible<_U1, _U2>())
>+ constexpr explicit(!_S_convertible<_U1, _U2>())
>+ pair(pair<_U1, _U2>&& __p)
>+ noexcept(_S_nothrow_constructible<_U1, _U2>())
>+ : first(std::forward<_U1>(__p.first)),
>+ second(std::forward<_U2>(__p.second))
>+ { }
>+
>+ private:
>+ /// @cond undocumented
>+ template<typename _U1, typename _U2>
>+ static constexpr bool
>+ _S_assignable()
>+ {
>+ if constexpr (is_assignable_v<_T1&, _U1>)
>+ return is_assignable_v<_T2&, _U2>;
>+ return false;
>+ }
>+
>+ template<typename _U1, typename _U2>
>+ static constexpr bool
>+ _S_nothrow_assignable()
>+ {
>+ if constexpr (is_nothrow_assignable_v<_T1&, _U1>)
>+ return is_nothrow_assignable_v<_T2&, _U2>;
>+ return false;
>+ }
>+ /// @endcond
>+
>+ public:
>+
>+ pair& operator=(const pair&) = delete;
>+
>+ /// Copy assignment operator
>+ constexpr pair&
>+ operator=(const pair& __p)
>+ noexcept(_S_nothrow_assignable<const _T1&, const _T2&>())
>+ requires (_S_assignable<const _T1&, const _T2&>())
>+ {
>+ first = __p.first;
>+ second = __p.second;
>+ return *this;
>+ }
>+
>+ /// Move assignment operator
>+ constexpr pair&
>+ operator=(pair&& __p)
>+ noexcept(_S_nothrow_assignable<_T1, _T2>())
>+ requires (_S_assignable<_T1, _T2>())
>+ {
>+ first = std::forward<first_type>(__p.first);
>+ second = std::forward<second_type>(__p.second);
>+ return *this;
>+ }
>+
>+ /// Converting assignment from a `pair<U1, U2>` lvalue
>+ template<typename _U1, typename _U2>
>+ constexpr pair&
>+ operator=(const pair<_U1, _U2>& __p)
>+ noexcept(_S_nothrow_assignable<const _U1&, const _U2&>())
>+ requires (_S_assignable<const _U1&, const _U2&>())
>+ {
>+ first = __p.first;
>+ second = __p.second;
>+ return *this;
>+ }
>+
>+ /// Converting assignment from a `pair<U1, U2>` rvalue
>+ template<typename _U1, typename _U2>
>+ constexpr pair&
>+ operator=(pair<_U1, _U2>&& __p)
>+ noexcept(_S_nothrow_assignable<_U1, _U2>())
>+ requires (_S_assignable<_U1, _U2>())
>+ {
>+ first = std::forward<_U1>(__p.first);
>+ second = std::forward<_U2>(__p.second);
>+ return *this;
>+ }
>+#else
>+ // C++11/14/17 implementation using enable_if, partially constexpr.
>
> /** The default constructor creates @c first and @c second using their
> * respective default constructors. */
>@@ -281,9 +462,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> explicit constexpr pair(const pair<_U1, _U2>& __p)
> : first(__p.first), second(__p.second) { }
>
>- constexpr pair(const pair&) = default; ///< Copy constructor
>- constexpr pair(pair&&) = default; ///< Move constructor
>-
> #if _GLIBCXX_USE_DEPRECATED
> private:
> /// @cond undocumented
>@@ -341,7 +519,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _GLIBCXX_DEPRECATED_SUGGEST("nullptr")
> explicit pair(__null_ptr_constant, _U2&& __y)
> : first(nullptr), second(std::forward<_U2>(__y)) { }
>-#endif // _GLIBCXX_USE_DEPRECATED
>+#endif
>
> template<typename _U1, typename _U2, typename
> enable_if<_PCCP::template
>@@ -382,10 +560,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> : first(std::forward<_U1>(__p.first)),
> second(std::forward<_U2>(__p.second)) { }
>
>- template<typename... _Args1, typename... _Args2>
>- _GLIBCXX20_CONSTEXPR
>- pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
>-
> _GLIBCXX20_CONSTEXPR pair&
> operator=(typename conditional<
> __and_<is_copy_assignable<_T1>,
>@@ -433,24 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> second = std::forward<_U2>(__p.second);
> return *this;
> }
>-
>- /// Swap the first members and then the second members.
>- _GLIBCXX20_CONSTEXPR void
>- swap(pair& __p)
>- noexcept(__and_<__is_nothrow_swappable<_T1>,
>- __is_nothrow_swappable<_T2>>::value)
>- {
>- using std::swap;
>- swap(first, __p.first);
>- swap(second, __p.second);
>- }
>-
>- private:
>- template<typename... _Args1, size_t... _Indexes1,
>- typename... _Args2, size_t... _Indexes2>
>- _GLIBCXX20_CONSTEXPR
>- pair(tuple<_Args1...>&, tuple<_Args2...>&,
>- _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
>+#endif // lib concepts
> #else
> // C++03 implementation
>
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>index d300292b463..a3e6a452583 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
>@@ -16,7 +16,7 @@
> // <http://www.gnu.org/licenses/>.
>
> // { dg-options "-Wdeprecated" }
>-// { dg-do compile { target c++11 } }
>+// { dg-do compile { target { c++11 && { ! c++20 } } } }
>
> #include <utility>
>
>diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>index 508ca32ecb7..ecd5acf9375 100644
>--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
>@@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {}
>
> std::pair<ExplicitDefault, int> f8()
> {
>- return {}; // { dg-error "could not convert" }
>+ return {}; // { dg-error "convert" }
> }
>
> std::pair<ExplicitDefaultDefault, int> f9()
> {
>- return {}; // { dg-error "could not convert" }
>+ return {}; // { dg-error "convert" }
> }
>
> void f10(std::pair<ExplicitDefault, int>) {}
>@@ -107,8 +107,8 @@ void test_arg_passing()
> f7({1,2});
> f7(std::pair<int, int>{});
> f7(std::pair<long, long>{});
>- f10({}); // { dg-error "could not convert" }
>- f11({}); // { dg-error "could not convert" }
>+ f10({}); // { dg-error "convert" }
>+ f11({}); // { dg-error "convert" }
> f10(std::pair<ExplicitDefault, int>{});
> f11(std::pair<ExplicitDefaultDefault, int>{});
> }
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-28 16:57 ` Jonathan Wakely
@ 2021-04-28 17:00 ` Jonathan Wakely
2021-04-28 17:11 ` Jonathan Wakely
0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:00 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 2100 bytes --]
On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>
>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>
>>>>The proposal sounds good to me.
>>>
>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>
>>Here's a patch to implement it, for stage 1.
>>
>> libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>>
>> This deprecates the non-standard std::pair constructors that support
>> construction from an rvalue and a literal zero used as a null pointer
>> constant. We can't just add the deprecated attribute to those
>> constructors, because they're currently used by correct code when they
>> are a better match than the constructors required by the standard e.g.
>>
>> int i = 0;
>> const int j = 0;
>> std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>>
>> This patch adjusts the parameter types and constraints of those
>> constructors so that they only get used for literal zeros, and the
>> pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>> for initializations that should be ill-formed we can add the deprecated
>> attribute.
>>
>> The deprecated attribute is used to suggest that the user code uses
>> nullptr, which avoids the problem of 0 deducing as int instead of a null
>> pointer constant.
>
>
>I've pushed this to trunk, after testing on powerpc64le-linux.
And here's the wwwdocs patch announcing the deprecation.
Pushed to wwwdocs.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 941 bytes --]
commit 24151c175c08222c268d9c37cc8fa255e2b2384c
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Apr 28 17:59:50 2021 +0100
Document deprecation of non-standard std::pair constructors for GCC 12
diff --git a/htdocs/gcc-12/changes.html b/htdocs/gcc-12/changes.html
index e0ac986e..912fc59b 100644
--- a/htdocs/gcc-12/changes.html
+++ b/htdocs/gcc-12/changes.html
@@ -30,6 +30,13 @@ a work-in-progress.</p>
<!-- .................................................................. -->
<h2>Caveats</h2>
<ul>
+ <li>
+ Two non-standard <tt>std::pair</tt> constructors have been deprecated.
+ These allowed the use of an rvalue and a literal <tt>0</tt> to construct
+ a pair containing a move-only type and a pointer. The <tt>nullptr</tt>
+ keyword should be used to initialize the pointer member instead of a
+ literal <tt>0</tt>, as this is portable to other C++ implementations.
+ </li>
<li>...</li>
</ul>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-28 17:00 ` Jonathan Wakely
@ 2021-04-28 17:11 ` Jonathan Wakely
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:11 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 2219 bytes --]
On 28/04/21 18:00 +0100, Jonathan Wakely wrote:
>On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>>
>>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>>
>>>>>The proposal sounds good to me.
>>>>
>>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>>
>>>Here's a patch to implement it, for stage 1.
>>>
>>> libstdc++: Deprecate non-standard std::pair constructors [PR 99957]
>>>
>>> This deprecates the non-standard std::pair constructors that support
>>> construction from an rvalue and a literal zero used as a null pointer
>>> constant. We can't just add the deprecated attribute to those
>>> constructors, because they're currently used by correct code when they
>>> are a better match than the constructors required by the standard e.g.
>>>
>>> int i = 0;
>>> const int j = 0;
>>> std::pair<int, int> p(i, j); // uses pair(U1&&, const int&)
>>>
>>> This patch adjusts the parameter types and constraints of those
>>> constructors so that they only get used for literal zeros, and the
>>> pair(U1&&, U2&&) constructor gets used otherwise. Once they're only used
>>> for initializations that should be ill-formed we can add the deprecated
>>> attribute.
>>>
>>> The deprecated attribute is used to suggest that the user code uses
>>> nullptr, which avoids the problem of 0 deducing as int instead of a null
>>> pointer constant.
>>
>>
>>I've pushed this to trunk, after testing on powerpc64le-linux.
>
>And here's the wwwdocs patch announcing the deprecation.
And the inevitable html validation fix.
Pushed to wwwdocs.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1250 bytes --]
commit b04fd89cb8bc48bad63fb59c91400bbf70e1f510
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Apr 28 18:08:33 2021 +0100
Replace <tt> elements with <code> for HTML5 compatibility
diff --git a/htdocs/gcc-12/changes.html b/htdocs/gcc-12/changes.html
index 912fc59b..23f71411 100644
--- a/htdocs/gcc-12/changes.html
+++ b/htdocs/gcc-12/changes.html
@@ -31,11 +31,12 @@ a work-in-progress.</p>
<h2>Caveats</h2>
<ul>
<li>
- Two non-standard <tt>std::pair</tt> constructors have been deprecated.
- These allowed the use of an rvalue and a literal <tt>0</tt> to construct
- a pair containing a move-only type and a pointer. The <tt>nullptr</tt>
- keyword should be used to initialize the pointer member instead of a
- literal <tt>0</tt>, as this is portable to other C++ implementations.
+ Two non-standard <code>std::pair</code> constructors have been deprecated.
+ These allowed the use of an rvalue and a literal <code>0</code> to
+ construct a pair containing a move-only type and a pointer.
+ The <code>nullptr</code> keyword should be used to initialize the pointer
+ member instead of a literal <code>0</code>, as this is portable to other
+ C++ implementations.
</li>
<li>...</li>
</ul>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC] Deprecate non-standard constructors in std::pair
2021-04-28 16:57 ` Jonathan Wakely
@ 2021-04-28 17:19 ` Jonathan Wakely
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Wakely @ 2021-04-28 17:19 UTC (permalink / raw)
To: Ville Voutilainen; +Cc: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 1318 bytes --]
On 28/04/21 17:57 +0100, Jonathan Wakely wrote:
>On 07/04/21 18:18 +0100, Jonathan Wakely wrote:
>>On 07/04/21 17:59 +0100, Jonathan Wakely wrote:
>>>On 07/04/21 13:46 +0100, Jonathan Wakely wrote:
>>>>On 07/04/21 15:41 +0300, Ville Voutilainen via Libstdc++ wrote:
>>>>>On Wed, 7 Apr 2021 at 15:31, Jonathan Wakely via Libstdc++
>>>>><libstdc++@gcc.gnu.org> wrote:
>>>>>>I propose that we deprecate the constructors for C++11/14/17/20 in
>>>>>>stage 1, and do not support them at all in C++23 mode once P1951 is
>>>>>>supported. I have a patch which I'll send in stage 1 (it also uses
>>>>>>C++20 concepts to simplify std::pair and fix PR 97930).
>>>>>>
>>>>>>After a period of deprecation we could remove them, and support P1951
>>>>>>for -std=gnu++11/14/17/20 too so that {} continues to work.
>>>>>
>>>>>The proposal sounds good to me.
>>>>
>>>>Thanks. I've created https://gcc.gnu.org/PR99957 so I don't forget.
>>>
>>>Here's a patch to implement it, for stage 1.
>>
>>And here's a patch to simplify the std::pair constraints using
>>concepts, also for consideration in stage 1.
>
>I've pushed this to trunk too, after testing on powerpc64le-linux.
And this adds a testcase to verify that the simplified version using
concepts actually fixes PR 97930, as claimed above.
Tested x86_64- linux, pushed to trunk.
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 1066 bytes --]
commit c8767ee9f9355a63bfeb8318df32bc39c5b0f3ad
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Apr 28 18:14:05 2021
libstdc++: Add testcase for std::pair as a structural type [PR 97930]
This PR was fixed by r12-221-ge1543e694dadf1ea70eb72325219bc0cdc914a35
(for compilers that support C++20 Concepts) so this adds the testcase.
libstdc++-v3/ChangeLog:
PR libstdc++/97930
* testsuite/20_util/pair/requirements/structural.cc: New test.
diff --git a/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc b/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc
new file mode 100644
index 00000000000..d4df20197ee
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/requirements/structural.cc
@@ -0,0 +1,9 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <utility>
+
+// C++20 20.4.2 [pairs.pair]
+// pair<T, U> is a structural type (13.2) if T and U are both structural types.
+
+template<std::pair<int, int>> struct S; // PR libstdc++/97930
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2021-04-28 17:19 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-07 12:30 [RFC] Deprecate non-standard constructors in std::pair Jonathan Wakely
2021-04-07 12:41 ` Ville Voutilainen
2021-04-07 12:46 ` Jonathan Wakely
2021-04-07 16:59 ` Jonathan Wakely
2021-04-07 17:18 ` Jonathan Wakely
2021-04-28 16:57 ` Jonathan Wakely
2021-04-28 17:19 ` Jonathan Wakely
2021-04-07 18:00 ` Jonathan Wakely
2021-04-07 18:17 ` Jonathan Wakely
2021-04-07 18:25 ` Ville Voutilainen
2021-04-07 18:26 ` Ville Voutilainen
2021-04-28 16:57 ` Jonathan Wakely
2021-04-28 17:00 ` Jonathan Wakely
2021-04-28 17:11 ` Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).