From 5840e3b8ff919c3ee1f9e7213ac612fe69c6f53a Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 6 Jul 2018 15:16:13 +0100 Subject: [PATCH] PR libstdc++/84928 use std::move in algorithms P0616R0 altered the effects of the algorithms to use std::move on the accumulator values (resolving LWG 2055). This implements the change for C++2a, but retains the previous behaviour for older standards. * include/bits/stl_numeric.h (_GLIBCXX_MOVE_IF_20): Define macro to conditionally move, according to __cplusplus value. (accumulate, inner_product, partial_sum, adjacent_difference): Use _GLIBCXX_MOVE_IF_20. * testsuite/26_numerics/accumulate/lwg2055.cc: New test. * testsuite/26_numerics/adjacent_difference/lwg2055.cc: New test. * testsuite/26_numerics/inner_product/lwg2055.cc: New test. * testsuite/26_numerics/partial_sum/lwg2055.cc: New test. From-SVN: r262477 --- libstdc++-v3/ChangeLog | 10 ++ libstdc++-v3/include/bits/stl_numeric.h | 27 ++-- .../26_numerics/accumulate/lwg2055.cc | 93 +++++++++++++ .../adjacent_difference/lwg2055.cc | 126 ++++++++++++++++++ .../26_numerics/inner_product/lwg2055.cc | 107 +++++++++++++++ .../26_numerics/partial_sum/lwg2055.cc | 125 +++++++++++++++++ 6 files changed, 480 insertions(+), 8 deletions(-) create mode 100644 libstdc++-v3/testsuite/26_numerics/accumulate/lwg2055.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/adjacent_difference/lwg2055.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/inner_product/lwg2055.cc create mode 100644 libstdc++-v3/testsuite/26_numerics/partial_sum/lwg2055.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 8519bea7213..df9a914a53e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,15 @@ 2018-07-06 Jonathan Wakely + PR libstdc++/84928 use std::move in algorithms + * include/bits/stl_numeric.h (_GLIBCXX_MOVE_IF_20): Define macro to + conditionally move, according to __cplusplus value. + (accumulate, inner_product, partial_sum, adjacent_difference): Use + _GLIBCXX_MOVE_IF_20. + * testsuite/26_numerics/accumulate/lwg2055.cc: New test. + * testsuite/26_numerics/adjacent_difference/lwg2055.cc: New test. + * testsuite/26_numerics/inner_product/lwg2055.cc: New test. + * testsuite/26_numerics/partial_sum/lwg2055.cc: New test. + * config/abi/pre/gnu.ver: Use wildcards to combine related patterns. P0935R0 Eradicating unnecessarily explicit default constructors diff --git a/libstdc++-v3/include/bits/stl_numeric.h b/libstdc++-v3/include/bits/stl_numeric.h index dcc29fe065c..f4f6f9ef5ae 100644 --- a/libstdc++-v3/include/bits/stl_numeric.h +++ b/libstdc++-v3/include/bits/stl_numeric.h @@ -104,6 +104,14 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_ALGO +#if __cplusplus > 201703L +// _GLIBCXX_RESOLVE_LIB_DEFECTS +// DR 2055. std::move in std::accumulate and other algorithms +# define _GLIBCXX_MOVE_IF_20(_E) std::move(_E) +#else +# define _GLIBCXX_MOVE_IF_20(_E) _E +#endif + /** * @brief Accumulate values in a range. * @@ -124,7 +132,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO __glibcxx_requires_valid_range(__first, __last); for (; __first != __last; ++__first) - __init = __init + *__first; + __init = _GLIBCXX_MOVE_IF_20(__init) + *__first; return __init; } @@ -151,7 +159,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO __glibcxx_requires_valid_range(__first, __last); for (; __first != __last; ++__first) - __init = __binary_op(__init, *__first); + __init = __binary_op(_GLIBCXX_MOVE_IF_20(__init), *__first); return __init; } @@ -180,7 +188,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO __glibcxx_requires_valid_range(__first1, __last1); for (; __first1 != __last1; ++__first1, (void)++__first2) - __init = __init + (*__first1 * *__first2); + __init = _GLIBCXX_MOVE_IF_20(__init) + (*__first1 * *__first2); return __init; } @@ -214,7 +222,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO __glibcxx_requires_valid_range(__first1, __last1); for (; __first1 != __last1; ++__first1, (void)++__first2) - __init = __binary_op1(__init, __binary_op2(*__first1, *__first2)); + __init = __binary_op1(_GLIBCXX_MOVE_IF_20(__init), + __binary_op2(*__first1, *__first2)); return __init; } @@ -251,7 +260,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO *__result = __value; while (++__first != __last) { - __value = __value + *__first; + __value = _GLIBCXX_MOVE_IF_20(__value) + *__first; *++__result = __value; } return ++__result; @@ -292,7 +301,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO *__result = __value; while (++__first != __last) { - __value = __binary_op(__value, *__first); + __value = __binary_op(_GLIBCXX_MOVE_IF_20(__value), *__first); *++__result = __value; } return ++__result; @@ -332,7 +341,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO while (++__first != __last) { _ValueType __tmp = *__first; - *++__result = __tmp - __value; + *++__result = __tmp - _GLIBCXX_MOVE_IF_20(__value); __value = _GLIBCXX_MOVE(__tmp); } return ++__result; @@ -375,12 +384,14 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO while (++__first != __last) { _ValueType __tmp = *__first; - *++__result = __binary_op(__tmp, __value); + *++__result = __binary_op(__tmp, _GLIBCXX_MOVE_IF_20(__value)); __value = _GLIBCXX_MOVE(__tmp); } return ++__result; } +#undef _GLIBCXX_MOVE_IF_20 + _GLIBCXX_END_NAMESPACE_ALGO } // namespace std diff --git a/libstdc++-v3/testsuite/26_numerics/accumulate/lwg2055.cc b/libstdc++-v3/testsuite/26_numerics/accumulate/lwg2055.cc new file mode 100644 index 00000000000..1686f393e7e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/accumulate/lwg2055.cc @@ -0,0 +1,93 @@ +// Copyright (C) 2018 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include + +struct Int +{ + Int(int v) : val(v) { } + + ~Int() = default; + + Int(const Int& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { ++copies; } + + Int(Int&& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { x.moved_from = true; } + + Int& operator=(const Int& x) + { + val = x.val; + copies = x.copies + 1; + moved_from = x.moved_from; + return *this; + } + + Int& operator=(Int&& x) + { + val = x.val; + copies = x.copies; + moved_from = x.moved_from; + x.moved_from = true; + return *this; + } + + int val = 0; + int copies = 0; + bool moved_from = false; +}; + +Int operator+(Int x, Int y) { x.val += y.val; return x; } + +struct Add +{ + Int operator()(Int x, Int y) const { x.val += y.val; return x; } +}; + +void +test01() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + Int res = std::accumulate(std::begin(i), std::end(i), Int{0}); + VERIFY( res.copies == 0 ); + VERIFY( !res.moved_from ); + for (const auto& r : i) + VERIFY( !r.moved_from ); +} + +void +test02() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + Int res = std::accumulate(std::begin(i), std::end(i), Int{0}, Add{}); + VERIFY( res.copies == 0 ); + VERIFY( !res.moved_from ); + for (const auto& r : i) + VERIFY( !r.moved_from ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/adjacent_difference/lwg2055.cc b/libstdc++-v3/testsuite/26_numerics/adjacent_difference/lwg2055.cc new file mode 100644 index 00000000000..766581a5d66 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/adjacent_difference/lwg2055.cc @@ -0,0 +1,126 @@ +// Copyright (C) 2018 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include + +struct Int +{ + Int(int v) : val(v) { } + + ~Int() = default; + + Int(const Int& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { ++copies; } + + Int(Int&& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { x.moved_from = true; } + + Int& operator=(const Int& x) + { + val = x.val; + copies = x.copies + 1; + moved_from = x.moved_from; + return *this; + } + + Int& operator=(Int&& x) + { + val = x.val; + copies = x.copies; + moved_from = x.moved_from; + x.moved_from = true; + return *this; + } + + int val = 0; + int copies = 0; + bool moved_from = false; +}; + +Int operator-(Int x, Int y) { x.val -= y.val; return x; } + +struct Subtract +{ + Int operator()(Int x, Int y) const { x.val -= y.val; return x; } +}; + +void +test01() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::adjacent_difference(std::begin(i), std::end(i), std::begin(i)); + for (const auto& r : i) + { + VERIFY( r.copies == 2 ); + VERIFY( !r.moved_from ); + } +} + +void +test02() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::adjacent_difference(std::begin(i), std::end(i), std::begin(i), + Subtract{}); + for (const auto& r : i) + { + VERIFY( r.copies == 2 ); + VERIFY( !r.moved_from ); + } +} + +void +test03() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::adjacent_difference(std::make_move_iterator(std::begin(i)), + std::make_move_iterator(std::end(i)), + std::begin(i)); + for (const auto& r : i) + { + VERIFY( r.copies == 1 ); + VERIFY( !r.moved_from ); + } +} + +void +test04() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::adjacent_difference(std::make_move_iterator(std::begin(i)), + std::make_move_iterator(std::end(i)), + std::begin(i), Subtract{}); + for (const auto& r : i) + { + VERIFY( r.copies == 1 ); + VERIFY( !r.moved_from ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/inner_product/lwg2055.cc b/libstdc++-v3/testsuite/26_numerics/inner_product/lwg2055.cc new file mode 100644 index 00000000000..7fea5d09476 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/inner_product/lwg2055.cc @@ -0,0 +1,107 @@ +// Copyright (C) 2018 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include + +struct Int +{ + Int(int v) : val(v) { } + + ~Int() = default; + + Int(const Int& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { ++copies; } + + Int(Int&& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { x.moved_from = true; } + + Int& operator=(const Int& x) + { + val = x.val; + copies = x.copies + 1; + moved_from = x.moved_from; + return *this; + } + + Int& operator=(Int&& x) + { + val = x.val; + copies = x.copies; + moved_from = x.moved_from; + x.moved_from = true; + return *this; + } + + int val = 0; + int copies = 0; + bool moved_from = false; +}; + +Int operator+(Int x, Int y) { x.val += y.val; return x; } +Int operator*(Int x, Int y) { x.val *= y.val; return x; } + +struct Add +{ + Int operator()(Int x, Int y) const { x.val += y.val; return x; } +}; + +struct Multiply +{ + Int operator()(Int x, Int y) const { x.val *= y.val; return x; } +}; + +void +test01() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + Int j[] = { 5, 6, 7, 8, 9 }; + Int res = std::inner_product(std::begin(i), std::end(i), std::begin(j), + Int{0}); + VERIFY( res.copies == 0 ); + VERIFY( !res.moved_from ); + for (const auto& r : i) + VERIFY( !r.moved_from ); + for (const auto& r : j) + VERIFY( !r.moved_from ); +} + +void +test02() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + Int j[] = { 5, 6, 7, 8, 9 }; + Int res = std::inner_product(std::begin(i), std::end(i), std::begin(j), + Int{0}, Add{}, Multiply{}); + VERIFY( res.copies == 0 ); + VERIFY( !res.moved_from ); + for (const auto& r : i) + VERIFY( !r.moved_from ); + for (const auto& r : j) + VERIFY( !r.moved_from ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/partial_sum/lwg2055.cc b/libstdc++-v3/testsuite/26_numerics/partial_sum/lwg2055.cc new file mode 100644 index 00000000000..871ddaf4c5e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/partial_sum/lwg2055.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2018 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include + +struct Int +{ + Int(int v) : val(v) { } + + ~Int() = default; + + Int(const Int& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { ++copies; } + + Int(Int&& x) : val(x.val), copies(x.copies), moved_from(x.moved_from) + { x.moved_from = true; } + + Int& operator=(const Int& x) + { + val = x.val; + copies = x.copies + 1; + moved_from = x.moved_from; + return *this; + } + + Int& operator=(Int&& x) + { + val = x.val; + copies = x.copies; + moved_from = x.moved_from; + x.moved_from = true; + return *this; + } + + int val = 0; + int copies = 0; + bool moved_from = false; +}; + +Int operator+(Int x, Int y) { x.val += y.val; return x; } + +struct Add +{ + Int operator()(Int x, Int y) const { x.val += y.val; return x; } +}; + +void +test01() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::partial_sum(std::begin(i), std::end(i), std::begin(i)); + for (const auto& r : i) + { + VERIFY( r.copies == 2 ); + VERIFY( !r.moved_from ); + } +} + +void +test02() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::partial_sum(std::begin(i), std::end(i), std::begin(i), Add{}); + for (const auto& r : i) + { + VERIFY( r.copies == 2 ); + VERIFY( !r.moved_from ); + } +} + +void +test03() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::partial_sum(std::make_move_iterator(std::begin(i)), + std::make_move_iterator(std::end(i)), + std::begin(i)); + for (const auto& r : i) + { + VERIFY( r.copies == 1 ); + VERIFY( !r.moved_from ); + } +} + +void +test04() +{ + Int i[] = { 0, 1, 2, 3, 4 }; + std::partial_sum(std::make_move_iterator(std::begin(i)), + std::make_move_iterator(std::end(i)), + std::begin(i), Add{}); + for (const auto& r : i) + { + VERIFY( r.copies == 1 ); + VERIFY( !r.moved_from ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} -- 2.30.2