【问题标题】:Parametrized traits c++参数化特征 C++
【发布时间】:2016-06-11 18:29:35
【问题描述】:

我正在学习书中的模板: C++ 模板,完整指南(Vandevoorde,Josuttis)。 在第 15.1.3 章。是下面的例子:

// traits/accum5.hpp 

#ifndef ACCUM_HPP 
#define ACCUM_HPP 

#include "accumtraits4.hpp" 

template <typename T, 
          typename AT = AccumulationTraits<T> > 
class Accum { 
  public: 
    static typename AT::AccT accum (T const* beg, T const* end) { 
        typename AT::AccT total = AT::zero(); 
        while (beg != end) { 
            total += *beg; 
            ++beg; 
        } 
        return total; 
    } 
}; 

#endif // ACCUM_HPP 

通常情况下,我们可以引入便利功能来简化界面:

template <typename T> 
inline 
typename AccumulationTraits<T>::AccT accum (T const* beg, 
                                            T const* end) 
{ 
    return Accum<T>::accum(beg, end); 
} 

template <typename Traits, typename T>
inline 
typename Traits::AccT accum (T const* beg, T const* end) 
{ 
    return Accum<T, Traits>::accum(beg, end); 
} 

这是我迷路的地方。 谁能解释第二个接口函数是如何工作的,为什么使用它是个好主意,它什么时候有用以及如何调用它?

谢谢!

【问题讨论】:

  • 好的。但是你的书怎么说?
  • 他们只是说:通常情况下,我们可以引入便利功能来简化界面

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


【解决方案1】:

为了具体回答问题,第二种形式允许您在调用站点选择特征类,例如:

#include <vector>
#include <iostream>

template<class T>
struct AccumulationTraits
{
    using AccT = T;
    static constexpr AccT zero() { return AccT(0); }
};

template <typename T,
typename AT = AccumulationTraits<T> >
class Accum {
public:
    static typename AT::AccT accum (T const* beg, T const* end) {
        typename AT::AccT total = AT::zero();
        while (beg != end) {
            total += *beg;
            ++beg;
        }
        return total;
    }
};

template <typename T>
inline
typename AccumulationTraits<T>::AccT accum (T const* beg,
                                            T const* end)
{
    return Accum<T>::accum(beg, end);
}

template <typename Traits, typename T>
inline
typename Traits::AccT accum (T const* beg, T const* end)
{
    return Accum<T, Traits>::accum(beg, end);
}



template<class T>
struct Doubler
{
    struct AccT {
        AccT(T t) : _t(t) {}
        operator T() const { return _t; }
        AccT& operator+=(T t) { _t += (t * 2); return *this; }
        T _t;
    };
    static constexpr AccT zero() { return AccT(0); }
};

int main()
{
    std::vector<int> v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    // first form selects default traits class
    auto a = accum(&v[0], &v[v.size()]);
    std::cout << a << std::endl;

    // second form selects any other traits class
    auto b = accum<Doubler<int>>(&v[0], &v[v.size()]);
    std::cout << b << std::endl;
}

预期输出:

45
90

【讨论】:

    【解决方案2】:

    关键是函数模板允许从函数调用表达式中推导模板参数,而类模板没有这样的类似物。

    如果你只有类模板,你需要自己拼出模板参数:

    int arr_sum = Accum<int>::accum(arr, arr + len);
    //                 ^^^^^
    

    通过用辅助函数模板补充类模板,我们可以让模板参数推导找出类型:

    int arr_sum = accum(arr, arr + len);    // Deduces T = int
    

    这是一种常见的模式。标准库中的示例为make_pairmake_tuplemake_optionalmake_move_iteratormake_reverse_iterator。例如:

    auto x = std::make_optional(10);
    
    // same as:
    std::optional<int> x(10);
    

    一个更引人注目的例子是make_reverse_iterator

    auto it = f();
    auto rit = std::make_reverse_iterator(it);
    
    // same as:
    std::map<int, std::string>::iterator it = f();
    std::reverse_iterator<std::map<int, std::string>::iterator> rit(it);
    

    在每种情况下,返回类型在某种程度上取决于类模板的特化,但类型推导辅助函数模板允许我们根本不必说出类模板名称。

    【讨论】:

    • 谢谢!是否可以提供有关如何使用第二个选项调用接口的示例?
    • @petertutuk:当然。你可以指定一些模板参数,剩下的留待推演,所以你可以说accum&lt;Trait&gt;(begin, end)
    猜你喜欢
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多