This is a generic question about templating, I am using advance as an example. Assume there are reasons I can't use ADL or hidden friends here.
The definition of std::advance
is:
template< class InputIt, class Distance >
constexpr void advance( InputIt& it, Distance n );
For the sake of argument, say I have a container H, with iterators which for some reason I want to overload std::advance
for:
template <typename T, class Allocator=std::allocator<T>>
class H
{
// etc
class iterator;
};
How does one design an advance overload to match that iterator, and have C correctly select the overload when calling advance on H's iterator class? The code below does not work because only H is specified in the templating, not the iterator itself:
template< template <typename, class> class container, class T, class Allocator, class Distance >
constexpr void advance( container<T, Allocator>::iterator& it, Distance n );
I can't quite work out how to specify the iterator specifically in the template line.
CodePudding user response:
A friend of mine came up with a solution based on tags (empty structs).
Basically, put a unique empty struct in the iterator class then use a few templates to build a C 20 concept out of them, then use that to match in the template overload:
template <typename T, class A = std::allocator<T>>
class H
{
// etc
public:
class iterator
{
public:
struct cheat_tag {};
// etc
};
};
template <class, class = void>
struct is_cheating : false_type {};
template <class T>
struct is_cheating<T, void_t<typename T::cheat_tag>> : true_type {};
template <class T>
concept h_iterator = is_cheating<T>::value;
namespace std
{
template <h_iterator T, class D>
constexpr void advance(T&, D)
{
// etc
}
}
Provided you match the signature of the template you're trying to overload exactly, this should work.