diff --git a/xml/issue3994.xml b/xml/issue3994.xml new file mode 100644 index 0000000000..b686732252 --- /dev/null +++ b/xml/issue3994.xml @@ -0,0 +1,70 @@ + + + + +<tt>adaptor(args...)(r)</tt> is not equivalent to <tt>std::bind_back(adaptor, args...)(r)</tt> +
+Hewill Kang +11 Oct 2023 +99 + + +

+ p8 specifies that: +

+

+The expression adaptor(args...) produces a range adaptor closure object f that is a perfect forwarding +call wrapper () with the following properties: +

+

+According to the subsequent description, it can be inferred that the behavior is similar to +std::bind_back(adaptor, args...) which also returns a perfect forwarding call wrapper. +

+Among them, "A perfect forwarding call wrapper is an argument forwarding call wrapper +that forwards its state entities to the underlying call expression" according to [func.require]/4, and call wrapper in [func.require]/3 is described as: +

+

+Every call wrapper () meets the Cpp17MoveConstructible and Cpp17Destructible requirements. +

+

+In order to conform with the specification, standard functions that return perfect forwarding call wrappers such as +std::bind_front/back and std::not_fn all Mandates that +(is_constructible_v<BoundArgs, Args> && ...) and +(is_move_constructible_v<BoundArgs> && ...) are each true, +the former condition corresponds to p8: +

+

+The expression adaptor(args...) is well-formed if and only if the initialization of the bound argument +entities of the result, as specified above, are all well-formed. +

+

+However, the latter does not have a corresponding description in <ranges>. In other words, range +adaptor objects do not explicitly indicate that the bound argument must be move-constructible. +This results in implementation divergence for some uncommon types (demo): +

+
+#include <ranges>
+#include <string_view>
+
+constexpr struct WeirdFive {
+  WeirdFive() = default;
+  WeirdFive(const WeirdFive&) = default;
+  constexpr operator int() const { return 5; }
+
+  WeirdFive(WeirdFive&&) = delete;
+} five;
+
+constexpr std::string_view sv{"hello"};
+static_assert(sv == std::views::take(five)(sv)); // libstdc++/libc++ reject, MSVC-STL accepts
+
+

+Above, libstdc++ always moves arguments into internal members, which leads to hard errors in the member initializer list; +libc++ uses std::bind_back for argument binding, which also leads to hard errors in the function body as the +former requires arguments to be move-constructible; MSVC-STL is the most compliant with current wording. +

+
+ + + + +