Can iter_swap be specialised? - c++

If I have a container std::vector<T*> items, I can create an IndirectIterator which wraps std::vector<T*>::iterator and allows iterating over T's rather than T*'s.
Can I specialise iter_swap for IndirectIterator to make standard algorithms (such as std::sort) swap items by pointer?
i.e., if I write the following, will it have any effect on standard algorithms?
namespace some_namespace
{
template <typename IterT>
class IndirectIterator
{
IterT m_base;
public:
typedef IterT base_iterator;
typedef /* ... */ reference;
/* ... */
reference operator*() const { **m_base; }
const base_iterator& base() const { return m_base; }
base_iterator& base() { return m_base; }
};
template <typename T>
void iter_swap(IndirectIterator<T>& a, IndirectIterator<T>& b)
{
using std::iter_swap;
iter_swap(a.base(), b.base());
}
}
The benefit of this specialisation is that it swaps pointers rather than full T instances, so it's faster (potentially).

As far as I can see, iter_swap is only used in std::reverse, and it does not mention any kind of argument-dependent lookup: it always uses std::iter_swap. And since you are not allowed to overload functions in the std namespace, you're out of luck.

Can I specialise iter_swap for IndirectIterator to make standard
algorithms (such as std::sort) swap items by pointer?
You can always make your overload/specialization. However your question is whether you can specialize iter_swap inside the namespace std.
I think the answer is unclear from the standard.
I have found that in some cases, I had to define a special iter_swap inside std so that std::sort uses it. In gcc std lib std::sort uses the qualified std::iter_swap.
This is probably a defect in std::sort.
IMO std::sort should call an unqualified swap_iter.
i.e., if I write the following, will it have any effect on standard
algorithms?
Not in GCC (at least) because standard algorithms use qualified std::iter_swap (bug?)
I don't think the standard is clear about it.

You are allowed to reopen std namespace and specialize templates inside std as long as you specialize them for user-defined types. In your case you can actually specialize std::iter_swap for your purposes, just make sure you are doing it in std namespace, not in your own namespace (as in your example). It is not very elegant, but it is allowed.

Starting in C++20, the answer is unambiguously "yes, you can customize your own ADL iter_swap." STL algorithms should use it, but aren't required to, and there is implementation divergence in practice.
For customizing a customization point, you should use the "hidden friend idiom":
namespace some_namespace {
template <class IterT>
class IndirectIterator {
IterT m_base;
public:
/* ... */
reference operator*() const { return **m_base; }
const base_iterator& base() const { return m_base; }
// "It's not a member, it's just a friend"
friend void iter_swap(IndirectIterator a, IndirectIterator b) {
using std::iter_swap;
iter_swap(a.base(), b.base());
}
};
}
Subtle change here: your iter_swap had been taking its arguments by mutable reference! That wouldn't work, e.g. if it were passed an IndirectIterator that was const, or that was an rvalue. Iterator parameters should always be taken by value. Similarly, there's no sense in providing a non-const version of base(). Your base() is a getter, not a setter; it should be a const member function for the same reason vector::size() is a const member function.
Finally, the big caveat here is that you must never customize iter_swap to do anything observably different from swapping the targets of the iterators. If you do that, then it's undefined behavior — all bets are off — the program could do anything. Formally, this requirement is given in [iterator.cust.swap]:
If the [iter_swap] function selected by overload resolution does not exchange the values denoted by E1 and E2, the program is ill-formed, no diagnostic required.
I believe your idea of having iter_swap swap the "first-level" targets rather than the "second-level" targets is perfectly kosher, as long as you're never going to rely on observing the difference. The standard library is still allowed to bypass iter_swap and just do swap(*it, *kt) if it feels like it; your code must be able to cope with that, or else "the program is ill-formed, no diagnostic required." (I.e., you must fully commit to the idea that swapping those first-level targets is tantamount to "exchanging the values of E1 and E2," and thus interchangeable with any other method of exchanging the values. You can't rely on your own method getting used.)
Here is a complete C++20 example on Godbolt. As of February 2023, here's where I see the customized iter_swap actually getting used:
Algorithm
libstdc++
libc++
Microsoft
std::ranges::partition
Yes
Yes
Yes
std::ranges::sort
No
No
Yes
std::partition
No
No
No
std::sort
No
No
No

Related

Why is deleting a function necessary when you're defining customization point object?

From libstdc++ <concepts> header:
namespace ranges
{
namespace __cust_swap
{
template<typename _Tp> void swap(_Tp&, _Tp&) = delete;
From MS-STL <concepts> header:
namespace ranges {
namespace _Swap {
template <class _Ty>
void swap(_Ty&, _Ty&) = delete;
I've never encountered = delete; outside the context where you want to prohibit the call to copy/move assignment/ctor.
I was curious if this was necessary, so I've commented out the = delete; part from the library like this:
// template<typename _Tp> void swap(_Tp&, _Tp&) = delete;
to see if the following test case compiles.
#include <concepts>
#include <iostream>
struct dummy {
friend void swap(dummy& a, dummy& b) {
std::cout << "ADL" << std::endl;
}
};
int main()
{
int a{};
int b{};
dummy c{};
dummy d{};
std::ranges::swap(a, b);
std::ranges::swap(c, d); // Ok. Prints "ADL" on console.
}
Not only it compiles, it seems to behave well by calling user defined swap for struct dummy. So I'm wondering,
What does template<typename _Tp> void swap(_Tp&, _Tp&) = delete; exactly do in this context?
On what occasion does this break without template<typename _Tp> void swap(_Tp&, _Tp&) = delete;?
TL;DR: It's there to keep from calling std::swap.
This is actually an explicit requirement of the ranges::swap customization point:
S is (void)swap(E1, E2) if E1 or E2 has class or enumeration type ([basic.compound]) and that expression is valid, with overload resolution performed in a context that includes this definition:
template<class T>
void swap(T&, T&) = delete;
So what does this do? To understand the point of this, we have to remember that the ranges namespace is actually the std::ranges namespace. That's important because a lot of stuff lives in the std namespace. Including this, declared in <utility>:
template< class T >
void swap( T& a, T& b );
There's probably a constexpr and noexcept on there somewhere, but that's not relevant for our needs.
std::ranges::swap, as a customization point, has a specific way it wants you to customize it. It wants you to provide a swap function that can be found via argument-dependent lookup. Which means that ranges::swap is going to find your swap function by doing this: swap(E1, E2).
That's fine, except for one problem: std::swap exists. In the pre-C++20 days, one valid way of making a type swappable was to provide a specialization for the std::swap template. So if you called std::swap directly to swap something, your specializations would be picked up and used.
ranges::swap does not want to use those. It has one customization mechanism, and it wants you to very definitely use that mechanism, not template specialization of std::swap.
However, because std::ranges::swap lives in the std namespace, unqualified calls to swap(E1, E2) can find std::swap. To avoid finding and using this overload, it poisons the overload by making visible a version that is = deleted. So if you don't provide an ADL-visible swap for your type, you get a hard error. A proper customization is also required to be more specialized (or more constrained) than the std::swap version, so that it can be considered a better overload match.
Note that ranges::begin/end and similar functions have similar wording to shut down similar problems with similarly named std:: functions.
There were two motivations for the poison pill overloads, most of which don't actually exist anymore but we still have them anyway.
swap / iter_swap
As described in P0370:
The Ranges TS has another customization point problem that N4381 does not cover: an implementation of the Ranges TS needs to co-exist alongside an implementation of the standard library. There’s little benefit to providing customization points with strong semantic constraints if ADL can result in calls to the customization points of the same name in namespace std. For example, consider the definition of the single-type Swappable concept:
namespace std { namespace experimental { namespace ranges { inline namespace v1 {
template <class T>
concept bool Swappable() {
return requires(T&& t, T&& u) {
(void)swap(std::forward<T>(t), std::forward<T>(u));
};
}
}}}}
unqualified name lookup for the name swap could find the unconstrained swap in namespace std either directly - it’s only a couple of hops up the namespace hierarchy - or via ADL if std is an associated namespace of T or U. If std::swap is unconstrained, the concept is “satisfied” for all types, and effectively useless. The Ranges TS deals with this problem by requiring changes to std::swap, a practice which has historically been forbidden for TSs. Applying similar constraints to all of the customization points defined in the TS by modifying the definitions in namespace std is an unsatisfactory solution, if not an altogether untenable.
The Range TS was built on C++14, where std::swap was unconstrained (std::swap didn't become constrained until P0185 was adopted for C++17), so it was important to make sure that Swappable didn't just trivially resolve to true for any type that had std as an associated namespace.
But now std::swap is constrained, so there's no need for the swap poison pill.
However, std::iter_swap is still unconstrained, so there is a need for that poison pill. However, that one could easily become constrained and then we would again have no need for a poison pill.
begin / end
As described in P0970:
For the sake of compatibility with std::begin and ease of migration, std::experimental::ranges::begin accepted rvalues and treated them the same as const lvalues. This behavior was deprecated because it is fundamentally unsound: any iterator returned by such an overload is highly likely to dangle after the full expression that contained the invocation ofbegin
Another problem, and one that until recently seemed unrelated to the design of begin, was that algorithms that return iterators will wrap those iterators in std::experimental::ranges::dangling<>if the range passed to them is an rvalue. This ignores the fact that for some range types — P0789’s subrange<>, in particular — the iterator’s validity does not depend on the range’s lifetime at all. In the case where a prvalue subrange<> is passed to an algorithm, returning a wrapped iterator is totally unnecessary.
[...]
We recognized that by removing the deprecated default support for rvalues from the range access customization points, we made design space for range authors to opt-in to this behavior for their range types, thereby communicating to the algorithms that an iterator can safely outlive its range type. This eliminates the need for dangling when passing an rvalue subrange, an important usage scenario.
The paper went on to propose a design for safe invocation of begin on rvalues as a non-member function that takes, specifically, an rvalue. The existence of the:
template <class T>
void begin(T&&) = delete;
overload:
gives std2::begin the property that, for some rvalue expression E of type T, the expression std2::begin(E) will not compile unless there is a free function begin findable by ADL that specifically accepts rvalues of type T, and that overload is prefered by partial ordering over the general void begin(T&&) “poison pill” overload.
For example, this would allow us to properly reject invoking ranges::begin on an rvalue of type std::vector<int>, even though the non-member std::begin(const C&) would be found by ADL.
But this paper also says:
The author believed that to fix the problem with subrange and dangling would require the addition of a new trait to give the authors of range types a way to say whether its iterators can safely outlive the range. That felt like a hack, and that feeling was reinforced by the author’s inability to pick a name for such a trait that was sufficiently succint and clear.
Since then, this functionality has become checked by a trait - which was first called enable_safe_range (P1858) and is now called enable_borrowed_range (LWG3379). So again, the poison pill here is no longer necessary.

How do you define emplace_back and other variadic template functions in a C++ concept?

I'm trying to define a C++ concept for standard library containers that allow push_back/emplace_back:
template <class ContainerType>
concept PushBackContainer = requires(ContainerType a)
{
requires SequenceContainer<ContainerType>;
{ a.push_back(typename ContainerType::const_reference& v) };
{ a.push_back(typename ContainerType::value_type&& v) };
// How do you define a variable templated function:
{ template< class... Args > a.emplace_back(Args&&... args) };
}
The problem I have is how do I define emplace_back with its variadic template arguments? I'm using Visual Studio 2019 but if this isn't supported I'd be interested in the correct syntax come the time it is.
Probably about the best that's worth doing is just a.emplace_back();.
Your push_back requirements don't have a correct syntax, either. I think you want:
template <class ContainerType>
concept PushBackContainer = requires(
ContainerType& a,
typename ContainerType::value_type const& cv,
typename ContainerType::value_type& v)
{
requires SequenceContainer<ContainerType>;
a.push_back(cv);
a.push_back(std::move(v));
a.emplace_back();
};
Requirements don't check for a function signature; they check for the validity of an expression (without instantiating more templates than necessary). If we had a class like:
class StrangeContainer {
public:
using value_type = std::string;
using const_reference = const value_type&;
private:
struct ValueHolder {
ValueHolder(const std::string& s) : value(s) {}
ValueHolder(std::string&& s) : value(std::move(s)) {}
std::string value;
};
public:
void push_back(ValueHolder);
template <typename ... Args>
void emplace_back(Args&&...);
};
then ignoring SequenceContainer requirements, PushBackContainer<StrangeContainer> would be true, and it would also satisfy the Standard's own requirements related to push_back. It satisfies the technical requirements, even though it has some surprising effects like the fact that push_back("") is ill-formed.
So for push_back, we're really just checking that it can be called with a const lvalue and with a non-const rvalue. (The Standard actually also requires that it can be called with a non-const lvalue and with a const rvalue, and these cases have the same behavior as when called with a const lvalue.)
(If you really wanted to test for an exact push_back signature, you could try static_cast<void (ContainerType::*)(typename ContainerType::value_type&&)>(&ContainerType::push_back); - but this is not recommended, since member functions in namespace std are not required to have signatures exactly as described, only to be callable with the same arguments as if declared as described.)
Also, the standard container class templates don't have any constraints on their push_back or emplace_back functions. Every instantiation of the templates which have push_back declares both overloads, whether or not the type is copyable and/or movable. If not, it would be an error to actually call or otherwise odr-use the push_back function, but it "exists" for purposes of requires-expressions and SFINAE contexts. Likewise, the emplace_back member template is declared to accept any number of arguments with any types and value categories, no matter whether they can be used as value_type constructor arguments or not.
So what we would want to test to find out if the container has an emplace_back with an essentially ordinary variadic function declaration would need to be phrased as: Can emplace_back be called with any number of arguments, with each having any possible type and each being either an lvalue or rvalue? I don't think there's any way to really answer that within C++, using requires-expressions, SFINAE tricks, or otherwise. So I would just do one simple test for existence of some sort of emplace_back, and that test might as well be as simple as possible: zero arguments.
You could get fancier and also test for some additional cases: Does emplace_back accept different numbers of arguments, up to some fixed maximum? Does it accept lvalue and rvalue arguments? Does it accept arguments of dummy struct types? Dummy struct types that aren't MoveConstructible? const, volatile, and const volatile types? All possible combinations of all of the above? But since you'll never cover all the cases, how much value does each partial enhancement like this really give, compared to the effort, complexity, and maintenance needed to add checks?

How can I check that assignment of const_reverse_iterator to reverse_iterator is invalid?

Consider the following:
using vector_type = std::vector<int>;
using const_iterator = typename vector_type::const_iterator;
using const_reverse_iterator = typename vector_type::const_reverse_iterator;
using iterator = typename vector_type::iterator;
using reverse_iterator = typename vector_type::reverse_iterator;
int main()
{
static_assert(!std::is_assignable_v<iterator, const_iterator>); // passes
static_assert(!std::is_assignable_v<reverse_iterator, const_reverse_iterator>); // fails
static_assert(std::is_assignable_v<reverse_iterator, const_reverse_iterator>); // passes
}
I can check that assignment of iterator{} = const_iterator{} is not valid, but not an assignment of reverse_iterator{} = const_reverse_iterator{} with this type trait.
This behavior is consistent across gcc 9.0.0, clang 8.0.0, and MSVC 19.00.23506
This is unfortunate, because the reality is that reverse_iterator{} = const_reverse_iterator{} doesn't actually compile with any of the above-mentioned compilers.
How can I reliably check such an assignment is invalid?
This behavior of the type trait implies that the expression
std::declval<reverse_iterator>() = std::declval<const_reverse_iterator>()
is well formed according to [meta.unary.prop], and this appears consistent with my own attempts at an is_assignable type trait.
This trait passes because the method exists that can be found via overload resolution and it is not deleted.
Actually calling it fails, because the implementation of the method contains code that isn't legal with those two types.
In C++, you cannot test if instantiating a method will result in a compile error, you can only test for the equivalent of overload resolution finding a solution.
The C++ language and standard library originally relied heavily on "well, the method body is only compiled in a template if called, so if the body is invalid the programmer will be told". More modern C++ (both inside and outside the standard library) uses SFINAE and other techniques to make a method "not participate in overload resolution" when its body would not compile.
The constructor of reverse iterator from other reverse iterators is the old style, and hasn't been updated to "not participate in overload resolution" quality.
From n4713, 27.5.1.3.1 [reverse.iter.cons]/3:
template<class U> constexpr reverse_iterator(const reverse_iterator<U>& u);
Effects: Initializes current with u.current.
Notice no mention of "does not participate in overload resolution" or similar words.
The only way to get a trait you want like this would be to
Change (well, fix) the C++ standard
Special case it
I'll leave 1. as an exercise. For 2., you know that reverse iterators are templates over forward iterators.
template<class...Ts>
struct my_trait:std::is_assignable<Ts...> {};
template<class T0, class T1>
struct my_trait<std::reverse_iterator<T0>, std::reverse_iterator<T1>>:
my_trait<T0, T1>
{};
and now my_trait is is_assignable except on reverse iterators, where it instead tests assignability of the contained iterators.
(As extra fun, a reverse reverse iterator will work with this trait).
I once had to do something very similar with std::vector<T>::operator<, which also blindly called T<T and didn't SFINAE disable it if that wasn't legal.
It may also be the case that a C++ standard library implementation can make a constructor which would not compile not participate in overload resolution. This change could break otherwise well formed programs, but only by things as ridiculous as your static assert (which would flip) or things logically equivalent.
It's just a question of constraints. Or lack thereof. Here's a reduced example with a different trait:
struct X {
template <typename T>
X(T v) : i(v) { }
int i;
};
static_assert(is_constructible_v<X, std::string>); // passes
X x("hello"s); // fails
Whenever people talk about being SFINAE-friendly - this is fundamentally what they're referring to. Ensuring that type traits give the correct answer. Here, X claims to be constructible from anything - but really it's not. is_constructible doesn't actually instantiate the entire construction, it just checks the expression validity - it's just a surface-level check. This is the problem that enable_if and later Concepts are intended to solve.
For libstdc++ specifically, we have:
template<typename _Iter>
_GLIBCXX17_CONSTEXPR
reverse_iterator(const reverse_iterator<_Iter>& __x)
: current(__x.base()) { }
There's no constraint on _Iter here, so is_constructible_v<reverse_iterator<T>, reverse_iterator<U>> is true for all pairs T, U, even if it's not actually constructible. The question uses assignable, but in this case, assignment would go through this constructor template, which is why I'm talking about construction.
Note that this is arguably a libstdc++ bug, and probably an oversight. There's even a comment that this should be constrained:
/**
* A %reverse_iterator across other types can be copied if the
* underlying %iterator can be converted to the type of #c current.
*/

Can I specialize std::begin and std::end for the return value of equal_range()?

The <algorithm> header provides std::equal_range(), as well as some containers having it as a member function. What bothers me with this function is that it returns a pair of iterators, making it tedious to iterate from the begin iterator to the end iterator. I'd like to be able to use std::begin() and std::end() so that I can use the C++11 range-based for-loop.
Now, I've heard contradictory information in regards to specializing std::begin() and std::end() - I've been told that adding anything to the std namespace results in undefined behavior, whereas I have also been told that you can provide your own specializations of std::begin() and std::end().
This is what I am doing right now:
namespace std
{
template<typename Iter, typename = typename iterator_traits<Iter>::iterator_category>
Iter begin(pair<Iter, Iter> const &p)
{
return p.first;
}
template<typename Iter, typename = typename iterator_traits<Iter>::iterator_category>
Iter end(pair<Iter, Iter> const &p)
{
return p.second;
}
}
And this does work: http://ideone.com/wHVfkh
But I am wondering, what are the downsides to doing this? Is there a better way to do this?
17.6.4.2.1/1 The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace
within namespace std unless otherwise specified. A program may add a
template specialization for any standard library template to namespace
std only if the declaration depends on a user-defined type and the
specialization meets the standard library requirements for the
original template and is not explicitly prohibited.
So yes, I believe that, technically, your code exhibits undefined behavior. Perhaps you can write a simple class that takes a pair of iterators in its constructor and implements begin() and end() methods. Then you can write something like
for (const auto& elem: as_range(equal_range(...))) {}

Wrapper contains() function for different containers

Inspired by contains(), I want do declare contains(), fitting misc containers.
// for std::vector, std::list, .. (1)
template<typename C, typename T>
bool contains(const C& container, const T& val)
{
return ::std::find(container.begin(), container.end(), val) != container.end();
}
// partial specialization for std::map (2)
template<typename K, typename V>
bool contains(const ::std::map<K, V>& container, const K& key)
{
// std::map.find() is better than std::find used in (1)
return container.find(key) != container.end();
}
Following question, I want to add the functions to namespace the arguments belongs too.
Questions:
Types of val from (1) and key from (2) are unknown. Does it mean I don't need to put the functions to any namespace or do I need to put them in std namespace, the containers belong to?
Is it possible to improve (2) it will preferred by compiler than (1) for std::map, std::set, boost::unordered_map, boost::unordered_set?
boost::algorithm::contains(). Do I need choose another name for my wrapper?
(1) Don't add anything to the std namespace
(2) As Space_C0wb0y points out, (2) is not a partial specialization, but an overload. I'm not sure if this is standard behaviour, but on VS2008 this is resolved correctly (the overloaded version is used for map). In either case, I think a slighly better version would be:
template<typename C>
bool contains(const C & mapContainer, const typename C::key_type & key)
{
// std::map.find() is better than std::find used in (1)
return mapContainer.find(key) != mapContainer.end();
}
This is a partial specialization (T is specialized to C::key_type) and would work for all types that has the key_type typedef (e.g. std::map, boost::unordered_map etc.)
(3) Put in separate namespace (see #Tony's answer)
Sidenote: I'm not sure I think these functions should have the same name. If I saw a function contains() accepting a map + another argument I could think that (1) the function checks if the map contains an entry for the given value (e.g. some key has the value provided) or (2) the function checks if there is such an entry in the map (e.g. the provided value is a pair). Instead I would call the function contains_key() for maps.
Edit:
After a bit of checking I suspect that the code above isn't compliant as it uses "template typedefs". This is not a standard C++ feature, but it seems that Visual Studio supports it in some cases. There's probably a workaround for this, but not one I'm clever enough to come up with now.
On number 3. you should place your version of contains in it's own namespace to avoid naming clashes. That's the purpose of namespaces.
Yes, keep them in a separate namespace.
The only thing to place in the same namespace as the argument type is operator overloads, to allow for Koenig (argument-dependent) lookup. These are not overloaded operators. End of story.
Note how this also answer question 2.: how you make the compiler prefer your implementations:
using myownversions::contains;
(assuming you named your namespace myownversions for now)
You must not put them into the std namespace. This is forbidden. You can put them into any namespace you like.
There is probably some SFINAE-magic you can do there, but I cannot come up with it in my own.
See #Tony's answer.