具有初始化列表的“内联”对象的静态声明

问题描述 投票:15回答:5

抱歉尴尬的标题,但我找不到更好的标题。

考虑这个示例代码(除了说明问题之外没有任何目的):

#include <vector>

void FooBar(int);

void func1()
{
    static std::vector<int> vec {1, 2, 3, 4};

    for (auto & v : vec)
      FooBar(v);
}

void func2()
{
    for (auto & v : std::vector<int> {1, 2, 3, 4})
      FooBar(v);
}

拆卸这个可以找到here

func1中,静态vec向量应该在启动时一次性构建。实际上上面提到的godbolt上的反汇编显示静态vec的初始化仅在func1的第一次调用时完成,而不是在启动时完成,但这不是重点。

现在考虑func2:这里向量在for语句中直接声明为“inline”(不确定这是如何实际调用的),但当然每次调用func2时都会构造该向量。

有没有办法在for语句中静态地声明该向量,就像for (auto & v : static std::vector<int> { 1, 2, 3, 4})一样,遗憾的是它不是合法的C ++。

c++ c++11 vector
5个回答
12
投票

这实际上对你没有帮助。但是你可以在C ++ 2a中做些什么。但是,它基于C ++ 14和C ++ 17中已有的东西。

C ++ 2a在基于循环的范围中添加了init-statement。这是新的一点,旧的一点是,它与今天定义的init-statement相同。定义如下([stmt.stmt]):

init-statement:
    expression-statement
    simple-declaration

我想要的是simple-declaration可能包含static限定符。是的,它会做你所期望的。所以在C ++ 2a中你可以写:

for (static std::vector<int> vec {1, 2, 3, 4}; int v : vec) {
   // Do things.
}

如果你想今天测试它的编译器支持,这里是一个C ++ 17 kludge:

if (static std::vector<int> vec {1, 2, 3, 4}; true) {
    // init-statement in if was added in C++17
  for(int v : vec)
    FooBar(v);
}

And its disassembly


8
投票

使用lambda的另一个选项(来自主要问题的评论):

void func()
{
    for(auto & v : ([]() -> std::vector<int>& { static std::vector<int> vec{1, 2, 3, 4}; return vec; })())
        FooBar(v);
}

3
投票

不,在当前的C ++(C ++ 17)中,这是不可能的。基于范围的for等效于以下伪代码:

{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
} 

这里没有init语句。 range_expression必须先验地初始化,或者必须传入要评估的braced-init-list(如std::vector<int>{1, 2, 3, 4})。这将在C ++ 20中更改。


2
投票

你可以做得更好:你可以把所有东西放在堆栈上。如果在-O2或更高版本上编译以下内容,它实际上将展开为4次调用FooBar()

另外,通过优化来查看反汇编并不是很有意义。

void func3()
{
    constexpr int vs [] = { 1, 2, 3, 4 };
    for ( int const v : vs )
        FooBar(v);
}

1
投票

实际上,C ++标准中没有任何内容禁止您正在寻找的优化。 std::vector<int,std::allocator>分配的堆内存确实可以被静态内存替换,而不会改变程序行为。但是如您的链接所示(即使添加了积极的优化选项),编译器也不会执行预期的优化。

因此你可以选择使用std::array而不是std::vectorstd::array很容易被优化器“理解”,here组装:

void FooBar(int);

void func2()
{
  for (auto & v : std::array<int,4> {1, 2, 3, 4})
    FooBar(v);
}

正如您在程序集中看到的那样,数组存储在静态内存中:

.LC0:
    .long   1
    .long   2
    .long   3
    .long   4

为了好玩,您可以使用使用静态内存的自定义分配器来获得良好的程序集,here the assembly

void FooBar(int i);

template<class T>
class static_alloc
  {
  static typename std::aligned_storage<4*sizeof(T),alignof(T)>::type buff;
  static bool allocated;

public:

  using value_type = T;

  bool operator==(const static_alloc&){
    return true;
  }
  bool operator!=(const static_alloc&){
    return false;
  }

  T* allocate(std::size_t n)
    {
    if (allocated) throw std::bad_alloc{};
    allocated=true;
    return reinterpret_cast<T*>(&buff);
    }
  void deallocate(T*,std::size_t)
    {
    allocated=false;
    }
  };
template<class T>
bool static_alloc<T>::allocated=false;
template<class T>
std::aligned_storage_t<4*sizeof(T),alignof(T)> static_alloc<T>::buff;


void func2()
{
   for (auto & v : std::vector<int,static_alloc<int>>{1,2,3,4})
      FooBar(v);
}
© www.soinside.com 2019 - 2024. All rights reserved.