Is there a way to wrap the pattern into a general, template function?
template <typename C>
auto Begin(C&& c) -> ??? {
using std::begin;
return begin(std::forward<C>(c));
}
The question here is how to write the return type of the function here?
The reason I want this is I want to write a template variable
template <typename C>
constexpr bool IsBidirectionalContainer =
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
decltype(std::begin(std::declval<C>()))>::iterator_category>::value;
The problem here is the std::begin
won't find custom overloads of begin
for C
via ADL. If anyone has workaround for this, it is welcome as well.
You need to wrap it inside another namespace, i.e:
namespace details {
using std::begin;
template <typename C>
auto Begin(C&& c) -> decltype(begin(std::forward<C>(c)))
{
return begin(std::forward<C>(c));
}
}
Then:
template <typename C>
constexpr bool IsBidirectionalContainer =
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
decltype(details::Begin(std::declval<C>()))>::iterator_category>::value;
If for some reason you refuse to define Begin
inside a namespace, you can just use a type alias to get around it.
namespace details {
using std::begin;
template <typename C>
using type = decltype(begin(std::forward<C>(c)));
}
template <typename C>
auto Begin(C&& c) -> details::type<C>
{
return begin(std::forward<C>(c));
}
Although that's probably more work than necessary. A forward declaration is probably sufficient.
If you’re going to wrap it, I think “why not use the latest best ideas?” Specifically, Eric Niebler makes an argument that these should be function-call objects, not functions!
Here is my fresh version using a C++17 compiler
// ===================
// would be in a reusable header
namespace twostep {
using std::begin;
using std::end;
inline auto Begin = [](auto&& r) -> decltype(begin(std::forward<decltype(r)>(r))) {
return begin(std::forward<decltype(r)>(r));
};
inline auto End = [](auto&& r) -> decltype(end(std::forward<decltype(r)>(r))) {
return end(std::forward<decltype(r)>(r));
};
}
using twostep::Begin;
using twostep::End;
Now since this is not replacing the implementation of the std::begin
/end
functions, I don’t get all the benefit that Eric points out. But, in addition to acting as a wrapper that does the two-step for me, as a normal template function implementation can do, it is itself immune from ADL.
If I refer to twostep::Begin
with qualification, it doesn’t matter. But if I import those into my own scope as this listing does to global scope, than an unqualified call to Begin(r)
will for-sure see the one that I have made visible at this point, and not find functions named Begin
in different namespaces because of all the types involved in r
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With