What's the difference between std::ranges::swap() and std::swap()? - c++

In C++20, there are two swap function templates: std::ranges::swap(T&&, U&&) and std::swap(T&, T&).
I just wonder:
What's the difference between they two?

std::swap has a problem. It's possible that there is a more efficient swap function that is not in the std namespace. You should enable ADL to find and use that if available and use std::swap otherwise. It's called "std 2-step":
using std::swap;
swap(a, b);
But std::ranges::swap doesn't have this problem and will call the version ADL would have called:
std::ranges::swap(a, b);
Here is a demonstration.

In rough terms: std::ranges::swap generalizes std::swap.
If T and U are the same, then it's equivalent to invoking std::swap.
Otherwise it will swap the arguments as two ranges - if they're ranges.
Otherwise it will perform a swap-like procedure with move assignment between the different types T and U.
If even that can't happen, it will fail to compile.
See the cppreference page for details.
I must say I find this function to be somewhat confusing and baroque, but then - maybe people more experienced with the range library develop an intuition as to why it makes sense.

Related

Which functions in standard C++ library should not be prefixed with std:: when used?

When I program in C++, instead of writing using namespace std;, I generally tend to use std:: prefixed components like std::cout, std::cin etc. But then I came across ADL and why you should use using std::swap;.
Many components of the standard library (within std) call swap in an unqualified manner to allow custom overloads for non-fundamental types to be called instead of this generic version: Custom overloads of swap declared in the same namespace as the type for which they are provided get selected through argument-dependent lookup over this generic version.
But in all sources about ADL, they only mention std::swap. Are there any other functions like this that I have to be beware of when using? Or for all other functions should I use fully qualified name? Or should I use unqualified name for every function in std::?
EDIT: (I wasn't clear when phrasing my initial question. Here is what I exactly intended when I was writing the question.)
Is there any other function in C++ standard libraries that is a popular candidate for ADL based customization much like std::swap, so that when I use them, I have to be cautious to use using std::foo; foo(); form instead of invoking std::foo(); directly?
Suppose you write a library that works with user supplied types. Then you might want to provide a default implementation to foo a bar. But at the same time you want to enable the user to provide their custom implementation because they might know better how to foo their custom bar.
Thats exactly what happens in the standard library with std::swap. The code that relies on ADL is inside libraries, it works with custom types, types it doesn't know until instantiated. And it can provide a default std::swap but at the same time allows the user to provide their own implementation.
In your user-code, when you know the type, then typically you want to know what function is called. Typically you do know if you want to call my::swap or std::swap and you do not need ADL to choose.
That being said, if you have to ask this quesiton then the answer is: Do not make use of ADL. (I know "If you have to ask.." is somewhat silly, but sometimes is applies just too well). Even in library code you do not always want to enable ADL, or allow it as customization. Even if you do write generic code, there might be situations where you need your way to foo a bar without allowing the user to customize it. It really depends.
Short answer: If you want to call std::swap then call std::swap. If you want to call my::swap then call my::swap. If you want to make use of ADL you will know what to do.
PS: There are more examples of ADL that you know but might not be aware of. You almost never use the std:: prefix when you call an operator overload for example. For example std::string operator+(const std::string&,const std::string&). You could write
std::string a,b;
auto c = std::operator+(a,b);
but you don't. You are used to write
auto c = a + b;
And this only works due to ADL.
PPS: Even for std::swap its not right that it "should not be prefixed with std::". As outlined above, sometimes you do want to make use of ADL and sometimes you don't. That decision is not per function, but per use case. If you write code that swaps two objects and for some reason you want to call std::swap and nothing else, then you call std::swap. If you want to make use of ADL then you make use of ADL.

Should static_pointer_cast calls be std:: qualified, or relied upon ADL?

static_pointer_cast resides in std namespace. Yet it takes pointers that are also in std namespace.
So both std:: qualified calls and unqualified calls are accepted.
Which one is the right, more idiomatic way to call static_pointer_cast?
Generally, if you know exactly which function you want to call, rather than allowing for customization, I would try to avoid ADL, implying avoiding unqualified calls.
In this case it is not expected for a library to provide its own static_pointer_cast for shared_ptrs of their own type. You always want to call the std version.
Calling std::static_pointer_cast directly guarantees that no other such static_pointer_cast will interfere.
If you include e.g. a library that also declared some function named static_pointer_cast which happens to be viable for your call and is a better fit or more specialized in overload resolution than the std one, you may accidentally call it instead if you use an unqualified call.
I think this is extremely unlikely to be a problem in the case of static_pointer_cast, because it is unlikely that a library will use that name in a way that the overload will be viable for a normal std::static_pointer_cast call and be at the same time a better match, all of it without realizing the implications.
But for example for std::launder this seems to have happened in some code bases (the other way around) when C++17 was introduced and the std overload turned out to be a better match than the library one in an unqualified call with a standard library type as argument.
If you look at for example C++ standard library implementations you will see that they follow this rule to avoid unqualified calls very strictly and use them only where the standard expects the user to be able to customize the call. Of course for non-library code especially this might not be as important of a consideration.
But also note that prior to C++20, if there is no function template named static_pointer_cast found by usual unqualified lookup, then an unqualified call using an explicit template argument list (which are required for static_pointer_cast) will fail anyway. So before C++20 you don't really have the choice to use the unqualified call without a prior using declaration to import the function.

What are customization point objects and how to use them?

The last draft of the c++ standard introduces the so-called "customization point objects" ([customization.point.object]),
which are widely used by the ranges library.
I seem to understand that they provide a way to write custom version of begin, swap, data, and the like, which are
found by the standard library by ADL. Is that correct?
How is this different from previous practice where a user defines an overload for e.g. begin for her type in her own
namespace? In particular, why are they objects?
What are customization point objects?
They are function object instances in namespace std that fulfill two objectives: first unconditionally trigger (conceptified) type requirements on the argument(s), then dispatch to the correct function in namespace std or via ADL.
In particular, why are they objects?
That's necessary to circumvent a second lookup phase that would directly bring in the user provided function via ADL (this should be postponed by design). See below for more details.
... and how to use them?
When developing an application: you mainly don't. This is a standard library feature, it will add concept checking to future customization points, hopefully resulting e.g. in clear error messages when you mess up template instantiations. However, with a qualified call to such a customization point, you can directly use it. Here's an example with an imaginary std::customization_point object that adheres to the design:
namespace a {
struct A {};
// Knows what to do with the argument, but doesn't check type requirements:
void customization_point(const A&);
}
// Does concept checking, then calls a::customization_point via ADL:
std::customization_point(a::A{});
This is currently not possible with e.g. std::swap, std::begin and the like.
Explanation (a summary of N4381)
Let me try to digest the proposal behind this section in the standard. There are two issues with "classical" customization points used by the standard library.
They are easy to get wrong. As an example, swapping objects in generic code is supposed to look like this
template<class T> void f(T& t1, T& t2)
{
using std::swap;
swap(t1, t2);
}
but making a qualified call to std::swap(t1, t2) instead is too simple - the user-provided
swap would never be called (see
N4381, Motivation and Scope)
More severely, there is no way to centralize (conceptified) constraints on types passed to such user provided functions (this is also why this topic gained importance with C++20). Again
from N4381:
Suppose that a future version of std::begin requires that its argument model a Range concept.
Adding such a constraint would have no effect on code that uses std::begin idiomatically:
using std::begin;
begin(a);
If the call to begin dispatches to a user-defined overload, then the constraint on std::begin
has been bypassed.
The solution that is described in the proposal mitigates both issues
by an approach like the following, imaginary implementation of std::begin.
namespace std {
namespace __detail {
/* Classical definitions of function templates "begin" for
raw arrays and ranges... */
struct __begin_fn {
/* Call operator template that performs concept checking and
* invokes begin(arg). This is the heart of the technique.
* Everyting from above is already in the __detail scope, but
* ADL is triggered, too. */
};
}
/* Thanks to #cpplearner for pointing out that the global
function object will be an inline variable: */
inline constexpr __detail::__begin_fn begin{};
}
First, a qualified call to e.g. std::begin(someObject) always detours via std::__detail::__begin_fn,
which is desired. For what happens with an unqualified call, I again refer to the original paper:
In the case that begin is called unqualified after bringing std::begin into scope, the situation
is different. In the first phase of lookup, the name begin will resolve to the global object
std::begin. Since lookup has found an object and not a function, the second phase of lookup is not
performed. In other words, if std::begin is an object, then using std::begin; begin(a); is
equivalent to std::begin(a); which, as we’ve already seen, does argument-dependent lookup on the
users’ behalf.
This way, concept checking can be performed within the function object in the std namespace,
before the ADL call to a user provided function is performed. There is no way to circumvent this.
"Customization point object" is a bit of a misnomer. Many - probably a majority - aren't actually customization points.
Things like ranges::begin, ranges::end, and ranges::swap are "true" CPOs. Calling one of those causes some complex metaprogramming to take place to figure out if there is a valid customized begin or end or swap to call, or if the default implementation should be used, or if the call should instead be ill-formed (in a SFINAE-friendly manner). Because a number of library concepts are defined in terms of CPO calls being valid (like Range and Swappable), correctly constrained generic code must use such CPOs. Of course, if you know the concrete type and another way to get an iterator out of it, feel free.
Things like ranges::cbegin are CPOs without the "CP" part. They always do the default thing, so it's not much of a customization point. Similarly, range adaptor objects are CPOs but there's nothing customizable about them. Classifying them as CPOs is more of a matter of consistency (for cbegin) or specification convenience (adaptors).
Finally, things like ranges::all_of are quasi-CPOs or niebloids. They are specified as function templates with special magical ADL-blocking properties and weasel wording to allow them to be implemented as function objects instead. This is primarily to prevent ADL picking up the unconstrained overload in namespace std when a constrained algorithm in std::ranges is called unqualified. Because the std::ranges algorithm accepts iterator-sentinel pairs, it's usually less specialized than its std counterpart and loses overload resolution as a result.

How does "using std::swap" enable ADL?

In What is the copy-and-swap idiom this example is shown:
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
// enable ADL (not necessary in our case, but good practice)
using std::swap;
// by swapping the members of two classes,
// the two classes are effectively swapped
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
How exactly does using std::swap enable ADL? ADL only requires an unqualified name. The only benefits I see for using std::swap is that since std::swap is a function template you can use a template argument list in the call (swap<int, int>(..)).
If that is not the case then what is using std::swap for?
Just wanted to add why this idiom is used at all, which seemed like the spirit of the original question.
This idiom is used within many std library classes where swap is implemented. From http://www.cplusplus.com/reference/algorithm/swap/:
Many components of the standard library (within std) call swap in an
unqualified manner to allow custom overloads for non-fundamental types
to be called instead of this generic version: Custom overloads of swap
declared in the same namespace as the type for which they are provided
get selected through argument-dependent lookup over this generic
version.
So the purpose of using an unqualified "swap" to swap member variables in the function you described is so that ADL can find customized swap functions for those classes (if they exist elsewhere).
Since these customized classes don't exist within the class you're referencing (mSize and mArray is a std::size_t and an int*, respectively, in the original example), and the std::swap works just fine, the author added a comment that this was not necessary in this case, but good practice. He would have gotten the same results had he explicitly called std::swap, as is pointed out in the previous answer.
Why is it good practice? Because if you have as members instances of classes for which custom swap is defined, you want the behavior to be this: check for a customized swap function...if it exists, use it, if it does not exist, use the std library functions. In the cases where there are no customized swap functions available, you want it to default to the simple std::swap implementation described in the link above. Hence the "using", to bring swap for built-in types into the namespace. But those will be tried last.
See also: https://stackoverflow.com/a/2684544/2012659
If for some reason you hate the "using std::swap", I suppose you could in theory resolve this manually by explicitly calling std::swap for everything you'd want to swap using std::swap and using the unqualified swap for every custom swap you know is defined (still found using ADL). But this is error prone ... if you didn't author those classes you may not know if a customized swap exists for it. And switching between std::swap and swap makes for confusing code. Better to let the compiler handle all of this.
The "enable ADL" comment applies to the transformation of
std::swap(first.mSize, second.mSize);
std::swap(first.mArray, second.mArray);
to
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
You're right, ADL only requires an unqualified name, but this is how the code is re-worked to use an unqualified name.
Just plain
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
wouldn't work, because for many types, ADL won't find std::swap, and no other usable swap implementation is in scope.

What's the point of iter_swap?

I was just wondering, why would anybody write this:
std::iter_swap(i, k);
instead of this?
std::swap(*i, *k); // saved a few keystrokes!
Then I looked into the implementation of iter_swap, and of course it only uses swap instead of std::swap since we're already in namespace std, anyway. That leads me to the next question:
Why would anybody write this:
using std::swap;
swap(a, b);
instead of this?
std::iter_swap(&a, &b); // saved an entire line of code!
Are there any important differences/issues I am overlooking here?
From the SGI docs (here):
[1] Strictly speaking, iter_swap is redundant. It exists only for technical reasons: in some circumstances, some compilers have difficulty performing the type deduction required to interpret swap(*a, *b).
To answer your second question, the using + swap allows the compiler to use user-defined swap functions that may be more efficient than the default implementation (by using ADL). Explicitly saying std::swap inhibits ADL and any custom swap methods it maybe have been able to find.
As for iter_swap it's presumably there to use in templates and clearly indicate intention rather than a swap that might indicate you expect a pointer.