解释来自cppreference的示例代码

时间:2015-06-03 08:51:29

标签: c++ templates template-meta-programming

有人可以从cppreference网站上解释我的样本中的几点吗? 该技术描述了函数重载取决于迭代器类型。 前两个typedef使用"使用"清楚地理解。 这些问题与alg功能有关:

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

    1. 你可以解释第二个函数alg中第三个模板参数的使用和那里的注释:
  3.   

    " typename = void> //虚拟值以避免模板重定义错误   "

    这段代码在这里(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());
    }
    

2 个答案:

答案 0 :(得分:3)

  1. typename = ...声明一个未命名的模板参数。客户端代码仍然可以覆盖它,但该参数不能在函数定义中使用。这是在这里使用的,因为第二个模板参数用于利用SFINAE,而不是计算出在定义中使用的类型。

  2. 更正,如果迭代器类型与预期的迭代器类型不同,则该函数将从重载候选集中删除。

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

  4. 使用默认值和虚拟参数对我来说非常难看,我更喜欢使用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{});
    }
    

答案 1 :(得分:1)

  1. 不,用户可以覆盖参数。命名参数 是可选的。如果你不能使用它,你不必这样做。
  2. 是。标签必须等于std::bidirectional_iterator_tag 第一次重载以及std::random_access_iterator_tag 第二个。
  3. 没有第三个模板参数,两个 函数声明是相同的,这是非法的。他们 要么必须根据参数,返回类型,名称或模板而不同 参数(在这里就是这种情况)。