首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++中的有效向量运算符/对临时对象的引用

C++中的有效向量运算符/对临时对象的引用
EN

Stack Overflow用户
提问于 2015-07-06 07:32:11
回答 2查看 1.2K关注 0票数 1

我正在尝试编写一个C++向量类,它存储一个数据数组,并允许在逐个元素的基础上执行数学操作。我希望以这样的方式实现这一点:表达式a = b + c + d应该只循环所有元素一次,并且直接将和b[i] + c[i] + d[i]写入a[i],而不创建中间向量。

我在写这样的东西:

代码语言:javascript
复制
template<class T, int N>
class VectorExpression {
  public:
    virtual T operator[] (int i) const = 0;

    virtual ~VectorExpression() {}
}

template<class T, int N>
class MyVector : public VectorExpression<T, N> {
    T data[N];

  public:
    T& operator[] (int i) { return data[i]; }
    T& const operator[] (int i) const { return data[i]; }

    MyVector<T,N>& operator=(const VectorExpression<T,N> &rhs) {
      for (int i = 0; i < N; ++i)
        data[i] = rhs[i];

      return *this;
    }
}

template<class T, int N>
class VectorSum : public VectorExpression<T, N> {
    VectorExpression<T,N> &a, &b;

  public:
    VectorSum(VectorExpression<T,N> &aa, VectorExpression<T,N> &bb)
    : a(aa), b(bb) {}

    T operator[] (int i) const { return a[i] + b[i]; }
}

template<class T, int N>
VectorSum<T,N> operator+(const VectorExpression<T,N> &a, 
        const VectorExpression<T,N> &b) 
{
  return VectorSum<T,N>(a, b);
}

int main() {
  MyVector<double,10> a, b, c, d;

  // Initialize b, c, d here

  a = b + c + d;

  return 0;
}

这个功能可能是由valarray类提供的,但这是因为我试图将其简化为一个最小的示例。

我将operator[]变成了虚拟的,因为它允许嵌套所有类型的表达式(例如,a = !(-b*c + d)),只要我定义所有操作符和类似于VectorSum的相应类。

我之所以使用引用,是因为普通变量不是多态变量,指针不能处理运算符重载。

现在我对此的问题是:

  • 在语句a = b + c + d;中,将分别创建两个临时VectorSum<double,10>对象来存储b + c(b+c) + d。他们能活足够长的时间让多态行为起作用吗?更具体地说,(b+c) + d将存储对b + c的引用,但是当调用operator=时,该对象是否仍然存在?根据this post,在operator=返回之前,所有临时程序都应该存在,但是对于较旧版本的C++,这也适用吗?
  • 如果没有,那是怎么做到的呢?我看到的唯一选择是使用VectorSum分配new对象,通过引用返回它们,然后在operator=函数中删除它们,但这似乎有点麻烦,而且可能效率要低得多。我也不确定它是否总是安全的。
  • (小问题)是否可以重写返回类型T of VectorExpression::operator[] by T& const in MyVector

编辑

我在operator+中出现了错误的参数类型:将它们从VectorSum改为VectorExpression

EN

回答 2

Stack Overflow用户

发布于 2015-07-08 23:10:17

我想出的是:

代码语言:javascript
复制
#include <iostream>
#include <initializer_list>
#include <algorithm>


template<class T, int N>
class VectorExpression {
public:
    virtual T operator[] (int i) = 0;
    virtual const T operator[] (int i) const = 0;

    virtual ~VectorExpression() {}
};


template<class T, int N>
class MyVector : public VectorExpression<T, N> {
  T data[N];

public:
  MyVector() {
    // initialize zero
    std::fill(std::begin(data), std::end(data), T());
  }

  MyVector(const std::initializer_list<T>& values) {
    // initialize from array initializer_list
    std::copy(std::begin(values), std::end(values), data);
  }

  MyVector(const VectorExpression<T,N>& rhs) { 
    for (int i = 0; i < N; ++i)
      data[i] = rhs[i];
  }

  MyVector<T,N>& operator=(const VectorExpression<T,N>& rhs) {
    for (int i = 0; i < N; ++i)
      data[i] = rhs[i];

    return *this;
  }

  T operator[] (int i) { return data[i]; }
  const T operator[] (int i) const { return data[i]; }

  friend std::ostream& operator<<(std::ostream& stream, MyVector& obj) { 
    stream << "[";
    for (int i = 0; i < N; ++i) { 
      stream << obj.data[i] << ", ";
    }
    stream << "]";

    return stream;
  }
};

template<class T, int N>
class VectorSum : public VectorExpression<T, N> {
  const MyVector<T,N> &a, &b;

public:
  VectorSum(const MyVector<T,N>& aa, const MyVector<T,N>& bb):
    a(aa), b(bb) {
  }

  T operator[] (int i) { return return a[i] + b[i]; }

  const T operator[] (int i) const { return a[i] + b[i]; }
};


template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
  return VectorSum<T,N>(a, b);
}


int main() {
  MyVector<double,3> a, b({1,2,3}), c({3,4,5}), d({4,5,6});

  a = b + c + d; 

  std::cout << b << std::endl;
  std::cout << c << std::endl;
  std::cout << d << std::endl;
  std::cout << "Result:\n" << a << std::endl;

  return 0;
}

输出:

代码语言:javascript
复制
[1, 2, 3, ]
[3, 4, 5, ]
[4, 5, 6, ]
Result:
[8, 11, 14, ]

我添加了一个initializer_list (C++11)构造函数和ostream操作符,纯粹是为了方便/演示。

由于您已经将operator[]定义为返回值,所以我无法在数据数组中设置用于测试的项(因为错误: lvalue需要作为赋值的左操作数);通常,这个操作符应该是引用的--但是在您的示例中,VectorSum::operator[]不能工作,因为这会因为返回对临时操作的引用而导致编译失败。

我还添加了一个副本构造函数,因为.

代码语言:javascript
复制
// this calls MyVector's copy constructor when assigned to 'main::a'
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
  return VectorSum<T,N>(a, b);   // implicit MyVector::copy constructor 
}

// this also calls MyVector's copy constructor (unless the copy constructor is defined explicit)
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
  MyVector<T,N> res = VectorSum<T,N>(a, b);
  return res;
}

// but this would call MyVector's assignment operator
template<class T, int N>
MyVector<T,N> operator+(const MyVector<T,N>& a, const MyVector<T,N>& b) {
  MyVector<T,N> res;
  res = VectorSum<T,N>(a, b);
  return res;
}

在回答你的问题时:

  1. 是的--如果您显式地定义了变量并返回该变量,它将如何表现?对于临时人员来说,这是同样的行为,除非没有可变的声明;
  2. 不适用
  3. 我提到了上面的内容--您不能使用引用,因为“返回对临时的引用”错误;但是,没有理由可以将T& operator[]添加到MyVector(即。(没有压倒一切)。

编辑:对评论的回答:

  1. 函数规范在重写时必须是相同的,包括返回类型。由于您已经在VectorExpression中定义了它按值返回,所以它必须在MyVector中按值返回。如果尝试将其更改为子类中的引用,则会得到一个编译错误:指定的冲突返回类型。因此,不可以用返回const的版本覆盖运算符const &而且,必须按值返回,因为MyVectorSum返回的是临时的{ a[i] + b[i] },所以不能返回对临时的引用。
  2. 抱歉,我弄错了。
  3. 因为:
代码语言:javascript
复制
- MyVector isn't a subtype of VectorSum - compile error ‘MyVector’ is not derived from ‘const VectorSum’
- I've also tried with VectorExpression but compile error: 'cannot allocate an object of abstract type' - because it's trying to return by value
- I chose MyVector since that's the type of your expected result. Yes it does those all those for loops but I can't see a way round that : there's three different array 'data' variables each of which need to be iterated into order to be accumulated. At some point in the code you will have to do the for loops.

  1. 明白,是的,我搞糊涂了。从岗位上移除。
票数 0
EN

Stack Overflow用户

发布于 2015-09-04 13:48:01

一开始我没有想到这一点,但是有一个虚拟的operator[]方法可能会通过避免向量大小的临时程序的3次循环和中间存储而扼杀我试图达到的效率。使用虚拟方法可以防止其内联,这意味着每次访问元素时都需要将其作为函数调用。

基于我从评论我的问题的人和谷歌获得的链接,我最终得到了以下解决方案,它避免了任何虚拟方法。

代码语言:javascript
复制
template<class T, int N, class V>
class VectorExpressionBase {
    V ref;

protected:
    explicit VectorExpressionBase(const V *ref) 
    : ref(const_cast<V*>(ref)) {}

public:
    T  operator[] (int i) const { return ref[i]; }
    T& operator[] (int i)       { return ref[i]; }
};

template<class T, int N>
class VectorExpressionBase<T,N,void> {
    T data[N];

protected:
    explicit VectorExpressionBase(const void*) {
        // Argument is unused but accepted to have uniform
        // calling syntax
    }

public:
    T  operator[] (int i) const { return data[i]; }
    T& operator[] (int i)       { return data[i]; }
};

template<class T, int N, class V>
class VectorExpression : public VectorExpressionBase<T,N,V> {
public:
    template<class V1>
    VectorExpression<T,N,V>& operator= (
        const VectorExpression<T,N,V1> &rhs) 
    {
        for (int i = 0; i < N; ++i)
            data[i] = rhs[i];

        return *this;
    }

    explicit VectorExpression(const V *ref = 0) 
    : VectorExpressionBase<T,N,V>(ref) {}

    // Can define all kinds of operators and functions here such as
    // +=, *=, unary + and -, max(), min(), sin(), cos(), ...
    // They would automatically apply to MyVector objects and to the
    // results of other operators and functions
};

template<class T, int N>
class MyVector : public VectorExpression<T,N,void> {
};

template<class T, int N, class VA, class VB>
class VectorSum {
    VectorExpression<T,N,VA> &a;
    VectorExpression<T,N,VB> &b;

  public:
    VectorSum(VectorExpression<T,N,VA> &aa, VectorExpression<T,N,VB> &bb)
    : a(aa), b(bb) {}

    T operator[] (int i) const { return a[i] + b[i]; }
};

template<class T, int N, class VA, class VB>
VectorExpression<T,N,VectorSum<T,N,VA,VB> >
operator+(const VectorExpression<T,N,VA> &a, 
          const VectorExpression<T,N,VB> &b) 
{
    VectorSum<T,N,VA,VB> sum(a, b);
    return VectorExpression<T,N,VectorSum<T,N,VA,VB> >(sum);
}

VectorExpression现在只是包装完成工作的类(在本例中是VectorSum)。这允许只为VectorExpression定义所有类型的函数和运算符,而不必为VectorSumVectorProduct等重载它们。

MyVector源于VectorExpression的一个特殊情况,它有一个专门的基类;这并不是必要的,但它很好,因为它使为VectorExpression定义的所有函数和操作符也可用于MyVector。通过使用只处理存储和[]运算符的简单基类VectorExpressionBase,所有其他运算符和方法都不需要在V = void的专门化中重复。

用户只需要知道MyVector<T,N>类(用于存储数据),也可能需要了解VectorExpression<T,N,V>,如果他们想定义其他函数和操作符的话。VectorExpressionBase和像VectorSum这样的类不需要在外部世界中可见。

我发现我最初的解决方案在概念上更清晰一些,因为每个类的含义都更清晰,而且它不需要模板参数V,但是这个方法更高效,因为它不需要任何虚拟函数,在某些情况下可能会产生很大的差异。

谢谢你给我指点正确的链接!

当然,所有这一切都不是新的,但我认为总结和解释一下是很好的。我希望它能帮助别人。

编辑

我将数据成员VectorExpressionBase<T,N,V>::ref的类型从V&更改为V。这是必要的,因为引用指向的临时V对象在计算VectorExpression时可能不再存在。例如,临时VectorSum对象在operator+函数返回时停止存在,使返回的VectorExpression对象毫无用处。

我还用一些构造函数完成了代码,并更正了operator+函数。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31240072

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档