Explain example code from cppreference

Can someone explain a few points to me in a sample from the cppreference site? This method describes the overloading of functions that depend on the type of the iterator. The first two "using" typedefs are easy to understand. The questions are related to alg functions:

  • in the template parameter list - "typename = ..." without the parameter name, does this mean that the default is used without the ability to overwrite it when the function is called?
  • As I understand it, using the second template parameter to the right - the function will only be generated if the type of the passed iterator type and the expected iterator tag is equal?

    1. You could explain the use of the third template parameter in the second alg function and comment there:

"typename = void> // dummy value to avoid the error of redefining the template"

A piece of code here ( http://en.cppreference.com/w/cpp/iterator/iterator_tags ):

template<typename Condition, typename T = void>
using EnableIf_t = typename std::enable_if<Condition::value, T>::type;

template<typename Iterator, typename IteratorTag>
using IsSameIteratorCond = 
    std::is_same<IteratorTag, 
    typename std::iterator_traits<Iterator>::iterator_category>;

template<
    typename BDIter, 
    typename = EnableIf_t<IsSameIteratorCond<BDIter, std::bidirectional_iterator_tag>>>
void alg(BDIter, BDIter)
{
    std::cout << "alg() called for bidirectional iterator\n";
}

template<
    typename RAIter, 
    typename = EnableIf_t<IsSameIteratorCond<RAIter, std::random_access_iterator_tag>>, 
    typename = void> // dummy value to avoid template re-definition error
void alg(RAIter, RAIter)
{
    std::cout << "alg() called for random-access iterator\n";
}

int main()
{
    std::vector<int> v;
    alg(v.begin(), v.end());

    std::list<int> l;
    alg(l.begin(), l.end());
}

      

+3


source to share


2 answers


  • typename = ...

    declares a parameter without a template name. Client code can still override it, but the parameter cannot be used in the function definition. This is used here because the second template parameter is used to use SFINAE and not to design the type used in the definition.

  • Correct, if the type of the iterator is not the same as expected, the function will be removed from the set of overload candidates.

  • A dummy parameter is needed because the defaults are not part of the template signature, so the two versions alg

    will try to define the same function template.

Using defaults and dummy parameters is pretty ugly for me, I'd rather use tag-dispatch:



template<typename BDIter>
void alg(BDIter, BDIter, std::bidirectional_iterator_tag)
{
    std::cout << "alg() called for bidirectional iterator\n";
}

template <typename RAIter>
void alg(RAIter, RAIter, std::random_access_iterator_tag)
{
    std::cout << "alg() called for random-access iterator\n";
}

template <typename It>
void alg(It a, It b)
{
    return alg(a, b, typename std::iterator_traits<It>::iterator_category{});
}

      

+3


source


  • No, the user can overwrite the parameter. Parameter naming is optional. You don't have to do this if you don't use it.
  • Yes. The tag must be equal std::bidirectional_iterator_tag

    for the first overload for the strike, and std::random_access_iterator_tag

    for the second.
  • Without the third template parameter, the two function declarations will be the same, which is illegal. They either have to differ in parameters, return type, name or template parameters (which is what they are).


+1


source







All Articles