向量的算术运算

4

假设我想为std::vector提供逐元素的算术运算符operator+=operator+,以便逐个元素地添加向量条目。通常情况下,我会看到operator+是这样实现的:operator+利用operator+=来实现。

#include <algorithm>
#include <vector>

template<class Type>
std::vector<Type> & operator+=(std::vector<Type> &x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    std::transform(std::begin(x), std::end(x), std::begin(y), std::begin(x), std::plus<Type>());
    return x;
}

template<class Type>
std::vector<Type> operator+(std::vector<Type> x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    return x += y;
}

int main() {
    std::vector<double> v0{1.0, 2.0, 3.0};
    auto v1 = v0;

    auto v2 = v0;
    v2 += v0; // yields [2, 4, 6]

    auto v3 = v0 + v1; // yields [2, 4, 6]

    return 0;
}

就性能而言,我猜想
template<class Type>
std::vector<Type> operator+(const std::vector<Type> &x, const std::vector<Type> &y) {
    // Checks for equal size of x and y omitted here...
    std::vector<Type> result;
    result.reserve(x.size());
    std::transform(std::begin(x), std::end(x), std::begin(y), std::back_inserter(result), std::plus<Type>());
    return result;
}

第二种方法更有效率,因为在进入函数时避免了初始化第一个参数的副本,而是直接将结果放入未初始化的内存块中。但是,是否值得实现第二个版本,还是可以假设编译器会进行优化呢?另外,我认为第二种选择比第一种不太通用。想象一下类似于以下情况:

#include <array>
#include <type_traits>

template<class Container, class Enable = void>
struct IsSequenceContainer: public std::false_type {
};

template<>
template<class Type, std::size_t size>
struct IsSequenceContainer<std::array<Type, size> >: public std::true_type {
};

template<>
template<class Type, class Allocator>
struct IsSequenceContainer<std::vector<Type, Allocator> >: public std::true_type {
};

// Use the following operations for std::array and std::vector
template<class Container>
typename std::enable_if<IsSequenceContainer<Container>::value, Container>::type operator+(Container x, const Container &y) {
    return x += y;
}

如果您为向量提供逐元素加法,请小心 - 请确保 operator+ 不会被误认为是连接运算符... - Toby Speight
还有std::valarray,但它不支持erase,insert等操作。 - melak47
@melak47 我知道 std::valarray,但实际上我更喜欢 std::vector 而不是 std::valarray,例如请参考这个帖子。 - Marcel
@TobySpeight 我知道关于容器的 operator+=operator+ 的歧义。然而,在我看来,只要你清晰地记录下你的意图并且根据你的应用(例如科学应用程序)确定,提供这样的运算符是完全合法的,即使在不同的上下文中它们可能有着不同的含义。 - Marcel
1
为标准容器添加运算符重载是有问题的。它不会被 ADL(因为只会查找namespace std;)找到;并且如果你在另一个任何定义了operator+=的范围内,它会隐藏这个重载函数。 - M.M
2个回答

3
与所有性能相关的事情一样:对程序进行剖析,看看会发生什么。 我猜编译器不会完全优化代码,而且这可能永远不重要。唯一知道确定的方法是尝试它。
通过+=实现+的优点在于这两个操作被认为是等效的。这使得出现错误的可能性较小。在放弃这种优势之前,您应该确保自己的优化是必要的。C++的习惯用法通常有很好的理由。

1

你有没有看过std::valarray?它已经提供了你所需的操作,而且你可能会从SIMD中受益。这可能是免费的性能++。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接