【问题标题】:How to use expression-templates for specific types?如何为特定类型使用表达式模板?
【发布时间】:2017-11-17 05:01:10
【问题描述】:

使用表达式模板时,如何创建特化?从Wikipedia example,我可以像这样制作一个 Vector sum 模板类:

template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2> > {
    E1 const& _u;
    E2 const& _v;
public:
    VecSum(E1 const& u, E2 const& v) : _u(u), _v(v) {
        assert(u.size() == v.size());
    }
    double operator[](size_t i) const { return _u[i] + _v[i]; }
    size_t size()               const { return _v.size(); }
};

template <typename E1, typename E2>
VecSum<E1,E2> const
operator+(E1 const& u, E2 const& v) {
   return VecSum<E1, E2>(u, v);
}

根据 Wikipedia,如果我有一个扩展 VecExpression&lt;Vector&gt;Vector 类和一个使用 [] 运算符和循环的 VecExpression 类的构造函数,这将允许循环合并,因此语句如下只使用一个循环:

Vector a = ...;
Vector b = ...;
Vector c = ...;
Vector d = a+b+c;

我明白为什么会这样,但我不确定如何将其扩展到标量。我希望能够向整个 Vector 添加一个标量(int、float 或 double),但我不确定如何执行此操作。我最好的猜测是为 VecSum 类创建专业化,例如:

template<typename E2> VecSum<int, E2>{ /*stuff goes here*/ }
template<typename E1> VecSum<E1, int>{ /*stuff goes here*/ }
template<typename E2> VecSum<float, E2>{ /*stuff goes here*/ }
template<typename E1> VecSum<E1, float>{ /*stuff goes here*/ }
template<typename E2> VecSum<double, E2>{ /*stuff goes here*/ }
template<typename E1> VecSum<E1, double>{ /*stuff goes here*/ }

但这似乎比必要的工作要多得多,还有其他解决方案吗?

【问题讨论】:

  • int + E2 应该是什么意思?你认为它的operator[] 应该是什么?
  • @PasserBy 我不是很清楚。在每个 VecSum 特化中,在 operator[] 代码中,我会简单地执行类似 _u+_v[i] 的操作,仅在非标量上使用 []

标签: c++ templates expression-templates


【解决方案1】:

只需使用 SFINAE 来检查类型是否为算术类型并根据需要进行专门化。

例子:

template <typename E1, typename E2, typename Enable = void > class VecSum;

template <typename E1, typename E2>
class VecSum< E1, E2,
      typename std::enable_if_t<!std::is_arithmetic<E1>::value && !std::is_arithmetic<E2>::value>
      > : public VecExpression<VecSum<E1, E2> >
{
    E1 const& _u;
    E2 const& _v;

    public:

    VecSum(E1 const& u, E2 const& v) : _u(u), _v(v)
    {
        assert(u.size() == v.size());
    }

    double operator[](size_t i) const { return _u[i] + _v[i]; }
    size_t size()               const { return _v.size(); }
};

template <typename E1, typename E2>
class VecSum < E1, E2,
      typename std::enable_if_t< std::is_arithmetic<E1>::value && !std::is_arithmetic<E2>::value>
      > : public VecExpression<VecSum<E1, E2> >
{
    E1 const& _u;
    E2 const& _v;

    public:

    VecSum(E1 const& u, E2 const& v) : _u(u), _v(v)
    {
    }

    double operator[](size_t i) const { return _u + _v[i]; }
    size_t size()               const { return _v.size(); }
};


template <typename E1, typename E2>
class VecSum < E1, E2,
      typename std::enable_if_t< !std::is_arithmetic<E1>::value && std::is_arithmetic<E2>::value>
      > : public VecExpression<VecSum<E1, E2> >
{
    E1 const& _u;
    E2 const& _v;

    public:

    VecSum(E1 const& u, E2 const& v) : _u(u), _v(v)
    {
    }

    double operator[](size_t i) const { return _u[i] + _v; }
    size_t size()               const { return _u.size(); }
};

int main(){
    Vec v0 = { 1, 2, 3 ,4 };
    Vec v1 = {10, 20,30,40 };
    Vec v2 = {100,200,300,400 };

    {

        Vec sum = v0+v1+v2;
        Vec v3(4);

        for(int i=0;i<4;++i)
            v3[i]=sum[i];


        for(unsigned int i=0;i<v3.size();++i)
            std::cout << v3[i] << std::endl;
    }

    std::cout << "with lhs skalar" << std::endl;

    {
        Vec sum = 5 + 50 + v1;
        Vec v3(4);

        for(int i=0;i<4;++i)
            v3[i]=sum[i];


        for(unsigned int i=0;i<v3.size();++i)
            std::cout << v3[i] << std::endl;

    }

    std::cout << "with rhs skalar" << std::endl;

    {
        Vec sum = v1 + 5 + 50 ;
        Vec v3(4);

        for(int i=0;i<4;++i)
            v3[i]=sum[i];


        for(unsigned int i=0;i<v3.size();++i)
            std::cout << v3[i] << std::endl;

    }

}

【讨论】:

    【解决方案2】:

    有什么替代方案:

    class AnySize { };
    bool operator==(AnySize, AnySize) { return true; }
    bool operator==(size_t, AnySize) { return true; }
    bool operator==(AnySize, size_t) { return true; }
    
    template <typename T>
    class Scalar {
        T _value;
    
    public:
        explicit Scalar(T value) : _value(value) {}
        T operator[](int) { return _value; }
        AnySize size() { return {}; }
    }
    

    您还需要更改VecExpression::size,以提供适当的大小,而不是总是在左侧

    AnySize size_for(AnySize, AnySize) { return {}; }
    size_t size_for(size_t lhs, AnySize) { return lhs; }
    size_t size_for(AnySize, size_t rhs) { return rhs; }
    size_t size_for(size_t lhs, size_t rhs) { return lhs; }
    
    auto VecExpression<E1,E2>::size() { return size_for(_u.size(), _v.size()); }
    

    【讨论】:

    • 虽然这确实有效,但最好将 size 函数添加到返回标志值(如 -1)的 Scalar 类中,因此我可以添加一个 if 语句以在 VecSum 中检查它构造函数。这样,如果有人尝试添加不同大小的向量,我仍然可以保持错误检查。
    • @BadProgrammer99 我更喜欢标记类型而不是标记值。还要记住std::size_t 是无符号的,所以-1 变成了一个很大的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 2017-12-01
    • 1970-01-01
    • 2021-05-09
    • 1970-01-01
    • 2011-04-18
    • 2021-08-30
    相关资源
    最近更新 更多