从功能返回容器:优化速度和现代风格

Returning container from function: optimizing speed and modern style

本文关键字:速度 风格 优化 功能 返回      更新时间:2023-10-16

不完全是一个问题,虽然只是一些我一直在思考的东西,如何编写这样的代码更优雅的风格,同时充分利用新的c++标准等。下面是示例

将斐波那契数列返回到最多N个值的容器中(对于那些不擅长数学的人来说,这只是将前两个值与前两个值相加等于1。(1,1,2,3,5,8,13,…)

示例:

std::vector<double> vec;
running_fibonacci_seq(vec,30000000);

1)

template <typename T, typename INT_TYPE>
    void running_fibonacci_seq(T& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        if (N>1) {
        coll[1] = 1;
        for (auto pos = coll.begin()+2;
            pos != coll.end();
            ++pos)
        {
            *pos = *(pos-1) + *(pos-2);
        }
        }
    }

2)相同,但使用rvalue &&而不是&1. e。

void running_fibonacci_seq(T&& coll, const INT_TYPE& N)

编辑:正如下面评论的用户所注意到的,右值和左值对计时没有影响——速度实际上是相同的,原因在评论

中讨论过

N = 30,000,000

的结果
Time taken for &:919.053ms
Time taken for &&: 800.046ms

首先,我知道这真的不是一个问题,但这些或哪个是最好的现代c++代码?使用右值引用(&&),似乎移动语义已经就绪,并且没有进行不必要的复制,这在时间上有了小小的改进(由于未来的实时应用程序开发,对我来说很重要)。一些具体的"问题"

a)传递一个容器(在我的例子中是vector)给函数作为参数并不是一个优雅的解决方案,如何真正使用右值。这是真的吗?如果是这样,那么在上面的例子中,右值如何真正显示它的轻?

b) col .resize(N);调用和N=1的情况下,是否有一种方法可以避免这些调用,从而使用户只使用函数而不动态创建vector的大小。模板元编程可以在这里使用,所以向量在编译时分配一个特定的大小?(即running_fibonacci_seq<30000000>),因为数字可能很大,是否有必要使用模板元编程,如果是这样,我们可以使用这个(链接)也

c)有没有更优雅的方法?我有一种感觉std::transform函数可以使用lambdas例如

    void running_fibonacci_seq(T&& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        coll[1] = 1;
        std::transform (coll.begin()+2,
                coll.end(),         // source
                coll.begin(),       // destination
                [????](????) {      // lambda as function object
                    return ????????;
                });
    }

[1] http://cpptruths.blogspot.co.uk/2011/07/want-speed-use-constexpr-meta.html

由于"引用塌陷",这段代码没有使用右值引用,也没有移动任何东西:

template <typename T, typename INT_TYPE>
void running_fibonacci_seq(T&& coll, const INT_TYPE& N);
running_fibonacci_seq(vec,30000000);

当你意识到这一点时,你所有的问题(和现有的评论)都变得毫无意义。

显而易见的答案:

std::vector<double> running_fibonacci_seq(uint32_t N);

为什么?

因为const-ness:

std::vector<double> const result = running_fibonacci_seq(....);

因为更简单的不变量:

void running_fibonacci_seq(std::vector<double>& t, uint32_t N) {
    // Oh, forgot to clear "t"!
    t.push_back(1);
    ...
}

速度呢?

有一种称为返回值优化的优化,它允许编译器在许多情况下省略副本(并直接在调用者的变量中构建结果)。c++标准特别允许,即使复制/移动构造函数有副作用

那么,为什么要传递"out"参数呢?

  • 你只能有一个返回值(叹号)
  • 您可能希望重用分配的资源(这里是t的内存缓冲区)

Profile this:

#include <vector>
#include <cstddef>
#include <type_traits>
template <typename Container>
Container generate_fibbonacci_sequence(std::size_t N)
{
    Container coll;
    coll.resize(N);
    coll[0] = 1;
    if (N>1) {
      coll[1] = 1;
      for (auto pos = coll.begin()+2;
        pos != coll.end();
        ++pos)
      {
        *pos = *(pos-1) + *(pos-2);
      }
    }
    return coll;
}
struct fibbo_maker {
  std::size_t N;
  fibbo_maker(std::size_t n):N(n) {}
  template<typename Container>
  operator Container() const {
    typedef typename std::remove_reference<Container>::type NRContainer;
    typedef typename std::decay<NRContainer>::type VContainer;
    return generate_fibbonacci_sequence<VContainer>(N);
  }
};
fibbo_maker make_fibbonacci_sequence( std::size_t N ) {
  return fibbo_maker(N);
}
int main() {
  std::vector<double> tmp = make_fibbonacci_sequence(30000000);
}

fibbo_maker的东西只是我聪明。但它可以让我推断出你想要的fibbo序列类型,而不必重复它。