解释 cppreference.com 中关于迭代器标签的示例代码

Explain the sample code from cppreference.com about iterator tags

有人可以向我解释 cppreference 站点示例中的几点吗? 该技术描述了函数重载取决于迭代器类型。 前两个带有 "using" 的 typedef 很容易理解。 与算法函数相关的问题:

  1. 在模板参数列表中 -"typename = ..." 没有参数名称,这是否意味着在函数调用中使用了默认值而无法覆盖它?
  2. 我是否正确理解第二个模板参数的使用 - 只有在传递的迭代器类型和预期的迭代器标记类型相等的情况下才会生成函数?

    1. 你能解释一下第二个函数 alg 中第三个模板参数的使用以及那里的评论吗:

"typename = void> // dummy value to avoid template re-definition error "

代码在这里(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());
}
  1. 不,用户可以覆盖参数。命名参数 是可选的。如果您不会使用它,则不必这样做。
  2. 是的。标签必须等于 std::bidirectional_iterator_tag 对于第一个过载,std::random_access_iterator_tag 第二个。
  3. 没有第三个模板参数,两个 函数声明将是相同的,这是非法的。他们 必须因参数、return 类型、名称或模板而异 参数(这里就是这种情况)。
  1. typename = ... 声明了一个未命名的模板参数。客户端代码仍然可以覆盖它,但不能在函数定义中使用该参数。此处使用它是因为第二个模板参数用于利用 SFINAE 而不是计算出要在定义中使用的类型。

  2. 正确,如果迭代器类型与预期的不一样,该函数将从重载候选集中移除。

  3. 需要虚拟参数,因为默认值不是模板签名的一部分,因此 alg 的两个版本将尝试定义相同的函数模板。

使用默认值和虚拟参数对我来说很丑陋,我更愿意使用标签调度:

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{});
}