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.
Related
I am implementing a sorting algorithm as personal training (no homework!).
I have the following code (excluding imports etc.):
template<class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp)
{
/* actual sorting code is here */
}
template<class RandomIt>
void sort(RandomIt first, RandomIt last)
{
std::function<bool(decltype(*first), decltype(*last))> comp = [](decltype(*first) a, decltype(*last) b)
{
return a < b;
};
sort (first, last, comp);
}
Trying to call this code with a test array
auto test_array_1 = std::make_unique <std::array < uint64_t,SORTING_TEST_LENGTH >> ();
std::copy(std::cbegin(*template_array), std::cend(*template_array), std::begin(*test_array_1));
sort(std::begin(*test_array_1), std::end(*test_array_1));
The compiler complains about "ambiguous call to overloaded function" (VC++ C2668). From my understanding the call should not be ambiguous though. Also giving the call in the second sort function the template parameters for the first sort function does not have any effect.
What am I missing here? Why does the compiler consider the second call "ambiguous"?
The problem is two fold.
First, sort is found via ADL, so you get two overloads, and they both match. In general, naming functions the same as std functions when you are not trying to ADL-overload is fraught, due to the possibility of ADL-induced ambiguity.
Now, this only happens when passing types from namespace std; Sometimes iterators are from this namespace, but not in this case : array uses raw pointer iterators. The trigger for ADL finding std::sort is the std::function.
Which brings me to the next problem: there is very little to be gained, and much to be lost, by std::function in the above code. Replace it with auto. Pass low level sorting algorithms an inlineable comparison object.
You still do not want to call it sort. If you call it sort you need to qualify calls with the namespace, or (sort) to block ADL.
The ADL rule is that both the "usual" functions, and functions in the namespace of arguments, and namespace of what arguments point to, and template arguments of arguments, etc, are considered for overload resolution. This is Argument Dependent Lookup, or ADL, or Koenig lookup. It means that namespace pollution of a kind can occur when using types from another namespace (which is sad), but it also makes some nice magic happen (like std::cout << "hello world\n";`)
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.
Reviewing the code of Microsoft's STL (specifically std::vector), I came across the following lines of code (irrelevant code replaced with /* ... */):
// CLASS TEMPLATE vector
template <class _Ty, class _Alloc = allocator<_Ty>>
class vector // varying size array of values
{
/* ... */
public:
/* ... */
using value_type = _Ty;
using allocator_type = _Alloc;
using pointer = typename _Alty_traits::pointer;
using const_pointer = typename _Alty_traits::const_pointer;
using reference = _Ty&;
using const_reference = const _Ty&;
using size_type = typename _Alty_traits::size_type;
using difference_type = typename _Alty_traits::difference_type;
/* ... */
};
I was wondering why the convention of assigning a type alias to a template type is used here?
I was wondering why the convention of assigning a type alias to a template type is used here?
Suppose you have a template function that accept an STL container (std::vector, std::deque, std::set, std::multi_set, ...)
template <typename T>
void foo (T const & t)
{
// ...
}
and that you need the type of contained values.
You can, inside foo(), simply write
using needed_type = typename T::value_type;
and this works for std::vector, std::deque, std::set, std::multi_set, std::array, std::map, std::multi_map, etc.
I was wondering why the convention of assigning a type alias to a
template type is used here?
Because it is
the standard way of doing,
makes less error-prone code,
less typing,
more readable, and
all the above makes life easy!
For instance, let's consider the const_pointer, public template alias of std::vector
using const_pointer = typename _Alty_traits::const_pointer;
At some point, you want to know this type and use in a function, how could have been possible without the above alias?
Of course, you can write
#include <memory> // std::allocator_traits
using const_pointer = typename std::allocator_traits<typename std::vector</*type*/>::allocator_type>::const_pointer;
anywhere in your program. But that tends to more error-prone situations (e.g. missing some typenames and so on), and more typing.
Therefore, it makes sense to collect those kinds of types of a container and provide public-aliases-types.
The C++ standard requires std::vector to provide a double-handful of nested names, and those are the ones the question is asking about. That's why value_type, allocator_type, pointer, etc. are there. You can use those names in your code when you need the types that they refer to. It's not at all uncommon to see, for example, std::vector<int>::iterator in user code to refer to the type of the iterator that std::vector<int> provides.
As to why they're written the way they are, that's a broader consistency issue. Templates throughout the Dinkumware implementation of the C++ standard library (which is what Microsoft ships) use _Ty as the base name for general types. When there are two types you'll see _Ty1 and _Ty2. When there's an internal typedef for a name it will be _Myty (for "My" type). That consistency makes maintaining the code easier.
That convention actually used for providing "traits". Naming for those types follows some convention that allows write functions like distance in terms of those traits and make it working for many containers.
And I'm pretty sure that having those types for standard C++ library is part of specification and they had to adhere it.
something in is messing out with std::swap and boost::unordered_set ?
I am experiencing some problems while using std::swap and boost::unordered_set. The following code generates a C2039 error on VC2017/VC2019. Comment the line #include and it works finely. Does someone has any clue about this problem ?
#include <vector>
#include <boost/unordered/unordered_set.hpp>
typedef boost::unordered_set<size_t> index_list_type;
int main()
{
index_list_type toto;
index_list_type tutu;
std::swap<index_list_type>(toto, tutu);
return 0;
}
MSVC\14.20.27508\include\vector(1702): error C2039: '_Alloc' : is not member of 'boost::unordered::unordered_set,std::equal_to,std::allocator>'
You should just use std::unordered_set, no need for boost.
Boost doesn't have to work with std algorithms. Check here expecially the "all standard containers specialize it in such a way that only a few internal pointers are swapped instead of their entire contents, making them operate in constant time." part.
Sure many things might be cross compatible between boost and std, but if all the tools you need are available in the same namespace, just use that one.
Boost is an entire ecosystem in itself, aimed at minimizing the reliance on the C++ standard library (remember that std::swap is available since C++11 only). As such, there is boost::swap(), which has overloads for all Boost data types, including boost::unordered_set:
// swap
template<typename Value, typename Hash, typename Pred, typename Alloc>
void swap(unordered_set<Value, Hash, Pred, Alloc>&,
unordered_set<Value, Hash, Pred, Alloc>&);
If you use std::swap then you should consider std::unordered_set, and vice versa if you can't or don't want to, then you should stick with Boost all the way. Try to minimize mixing std and boost.
As to the error - this looks like is a bug in the MSVC standard library; the std::swap<vector> implementation is not explicitly restricted to vectors, which is wrong since it fails whenever the type is specified explicitly.
It looks roughly like this:
template<class T>
class _Vb_reference
{
using _Alvbase = typename T::_Alloc;
};
template<class T>
void swap(_Vb_reference<T> _Left, _Vb_reference<T> _Right)
{
}
That only works when the arguments are deduced, as in
std::swap(toto, tutu); // _Vb_reference<T> is non-deduced context
But fails when the type is specified explicitly:
std::swap<index_list_type>(toto, tutu); // instantiation of _Vb_reference<index_list_type> fails = hard error
A better implementation would include some SFINAE to restrict the template to _Vb_reference<T> types.
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