The established idiom for invoking swap
is:
using std::swap
swap(foo, bar);
This way, swap
can be overloaded for user
the documentation of swap
specifies that the idiom you refer to is common practice in the stl library
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.
No such thing is present in the documentation for begin
and end
.
For this reason, you can definitely use the
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
calling convention, but you must be aware that this is a convention which doesn't apply to e.g. standard algorithms but to your code only.
Disclaimer: For the pedantic types (or pedants, if you want to be pedantic...), I generally refer to the word "overload" here as "Create functions that have the names begin
and end
and do using std::begin; using std::end;
.", which, believe me, is not tedious for me to write at all, but is very hard to read and is redundant to read. :p.
I'll basically give you the possible use-cases of such technique, and later my conclusion.
begin
and end
methods do not act like those of the standard containersOne situation where you may need to overload the std::begin
and std::end
functions is when you're using the begin
and end
methods of your type in a different way other than to provide iterator-like access to the elements of an object, and want to have overloads of std::begin
and std::end
call the begin and end methods used for iteration.
struct weird_container {
void begin() { std::cout << "Start annoying user." }
void end() { std::cout << "Stop annoying user." }
iterator iter_begin() { /* return begin iterator */ }
iterator iter_end() { /* return end iterator */ }
};
auto begin(weird_container& c) {
return c.iter_begin();
}
auto end(weird_container& c) {
return c.iter_end();
}
However, you wouldn't and shouldn't do such a crazy thing as range-for would break if used with an object of weird_container
, as per rules of range-for, the weird_container::begin()
and weird_container::end()
methods would be found before the stand-alone function variants.
This case therefore brings an argument not to use what you have proposed, as it would break one very useful feature of the language.
begin
and end
methods aren't defined at allAnother case is when you don't define the begin
and end
methods. This is a more common and applicable case, when you want to extend your type to be iteratable without modifying the class interface.
struct good_ol_type {
...
some_container& get_data();
...
};
auto begin(good_ol_type& x) {
return x.get_data().begin();
}
auto end(good_ol_type& x) {
return x.get_data().end();
}
This would enable you to use some nifty features on good_ol_type
(algorithms, range-for, etc) without actually modifying its interface! This is in line with Herb Sutter's recommendation of extending the functionality of types through non-member non-friend functions.
This is the good case, the one where you actually want to overload std:;begin
and std::end
.
As I haven't ever seen someone do something like that of the first case (except for my example), then you'd really want to use what you've proposed and overload std::begin
and std::end
wherever applicable.
I did not include here the case where you defined both begin
and end
methods, and begin
and end
functions that does different things than the methods. I believe such a situation is contrived, ill-formed and/or done by a programmer who haven't had much experience delving into the debugger or reading novel template errors.
Using a using
-declaration like that is the correct way IMO. It's also what the standard does with the range for loop: if there is no begin
or end
members present then it will call begin(x)
and end(x)
with std
as an associated namespace (i.e. it will find std::begin
and std::end
if ADL doesn't find non-member begin
and end
).
If you find that writing using std::begin; using std::end;
all the time is tedious then you can use the adl_begin
and adl_end
functions below:
namespace aux {
using std::begin;
using std::end;
template<class T>
auto adl_begin(T&& x) -> decltype(begin(std::forward<T>(x)));
template<class T>
auto adl_end(T&& x) -> decltype(end(std::forward<T>(x)));
template<class T>
constexpr bool is_array()
{
using type = typename std::remove_reference<T>::type;
return std::is_array<type>::value;
}
} // namespace aux
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_begin(T&& x) -> decltype(aux::adl_begin(std::forward<T>(x)))
{
using std::begin;
return begin(std::forward<T>(x));
}
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_end(T&& x) -> decltype(aux::adl_end(std::forward<T>(x)))
{
using std::end;
return end(std::forward<T>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
This code is pretty monstrous. Hopefully with C++14 this can become less arcane:
template<typename T>
concept bool Not_array()
{
using type = std::remove_reference_t<T>;
return !std::is_array<type>::value;
}
decltype(auto) adl_begin(Not_array&& x)
{
using std::begin;
return begin(std::forward<Not_array>(x));
}
decltype(auto) adl_end(Not_array&& x)
{
using std::end;
return end(std::forward<Not_array>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
If your some_container is standard container, std:: prefix is needless
#include <iostream>
#include <vector>
#include <algorithm>
int main(){
std::vector<int>v { 1, 7, 1, 3, 6, 7 };
std::sort( begin(v), end(v) ); // here ADL search finds std::begin, std::end
}