首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >现代C++数组参数

现代C++数组参数
EN

Stack Overflow用户
提问于 2018-04-25 15:47:46
回答 3查看 2.3K关注 0票数 3

我希望在现代C++中将可变大小的小数组作为参数传递,即:

代码语言:javascript
复制
func({1,2,3,4});

而且它应该尽可能快,最好没有堆分配。

我尝试过的事情:

C样式数组

代码语言:javascript
复制
void func(int * arr, int arrayCount) {
   for (int i = 0; i < arrayCount; i++) {
      //use arr[i];
   }
}

int arr[] = {1,2,3,4};
func(arr, 4);

这是快速和有效的,但增加了一个额外的arrayCount变量,它容易出现用户错误,函数调用现在被分成两行。

性病载体

代码语言:javascript
复制
void func(vector<int> arr) {
    for (int &a : arr) {
      //use a
    }
}

func({1,2,3});

这是非常干净的,但由于将数据复制到堆中,所以速度非常慢。将签名更改为使用通用参考文件:

代码语言:javascript
复制
void func(vector<int> && arr) {

似乎没什么区别。

initializer_list

代码语言:javascript
复制
void func(initializer_list<int> arr) {
    for (int &a : arr) {
      //use a
    }
}

func({1,2,3});

这是一个50倍的速度提高了矢量(大约)!使用A&还是没有什么区别,但是由于initializer_list创建和迭代的开销,它仍然比C样式的语法慢5~10倍。对于许多用例来说仍然是可以接受的,但是开销似乎有点不必要。

考虑到编译器可能变得更聪明了,我尝试了以下几点:

std阵列

代码语言:javascript
复制
template <int arrayCount>
void func(array<int, arrayCount> arr) {

  for (int &a : arr) {
    //use a
  }

}

编译器无法从调用中推断出大小,因此有必要编写

代码语言:javascript
复制
func<3>({1,2,3});

在任何情况下,这都不会比初始化程序列表快。

由于多种原因,多样性模板是不可能的,因为它们不支持多个数组参数,而且语法很难导航。

有没有一种既能获得简洁的语法又能获得快速性能的方法?似乎添加一些语法糖来包装C样式数组表示法似乎很容易。

Clarification

这些测试是在默认优化(O2)的MSVC 2017中执行的:

代码语言:javascript
复制
timer.start();
for(int i = 0; i < 100000; i++) {
   func({i, i+2, i+3, i+4});
}
timer.stop();

或者是c风格:

代码语言:javascript
复制
for (int i = 0; i < 100000; i++) {
   int arr[] = {i, i+2, i+3, i+4};
   func(arr, 4);
}

将传递的值添加到静态变量中。

评论

有关使用泛型传递数组的语法,请参见下面的答案,该语法避免STL,性能与C样式选项相似,但其处理空数组的能力有限。

我的理解是,在大多数用例中,initalizer_list表示法可能足够快,应该避免使用向量表示法,当性能绝对关键时,根据用例考虑泛型数组方法或C样式数组。MSVC并没有像GCC/Clang那样对它们进行优化。

EN

回答 3

Stack Overflow用户

发布于 2018-04-25 15:57:20

试试这个:

代码语言:javascript
复制
template< typename T, size_t N>
void func( T const (&arr)[ N ]  )
{...}

void func() // overloaded function for empty parameter
{...}

编译器将从传递给函数的数组中自动推断类型和数组大小。数组必须是一个完整的类型,而不是指针,而维度必须在调用位置知道。

编辑:如果不能使用没有参数的函数,则应该考虑标记分派:

代码语言:javascript
复制
class Empty_List {};

static constexpr Empty_List empty;

void func( Empty_List ) 
{ ... }

这应该称为:

代码语言:javascript
复制
 func( empty ); // option 1
 func( {} );    // option 2
票数 3
EN

Stack Overflow用户

发布于 2018-04-26 15:28:24

有了C++17,就可以很容易地用可变的非类型模板来替换这样的小数组。也许值得考虑这样的事情:

代码语言:javascript
复制
template <std::size_t... Ns>
using A = std::index_sequence<Ns...>; // syntactic sugar

template <std::size_t... Ns>
constexpr void func(A<Ns...>) {
    (do_something(Ns), ...); // fold expression <=> for (int a: arr)
}
票数 1
EN

Stack Overflow用户

发布于 2018-04-26 16:21:48

在C++中,常见的成语是span,有时称为span、ref或视图,如array_view、string_view等。实际上,它是C样式数组中使用的指针和大小参数的捆绑,但在提供std::arraystd::vector接口的C++类中。它是对另一个连续容器中数据的不拥有引用。有些实现只对const引用使用spans,但另一些则不使用。

这个成语为您提供了与您的std::vector大小写相同的代码,但是它与C风格的数组大小写一样高效,因为没有动态内存分配和复制。

这个习语已经变得如此普遍,以至于它是C++核心指南支持库的一部分,而且一个版本甚至可能会进入C++标准库。

如果你没有一个已经提供这个成语的库,那么你自己的库就不难了。您可以创建一个类模板,该模板作为成员具有指针和元素计数。您可以添加构造函数,以便轻松地将容器转换到此表示形式,并使operator[]过载以提供访问。

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

https://stackoverflow.com/questions/50026430

复制
相关文章

相似问题

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