"串联"具有初始化列表

时间:2017-12-20 16:28:35

标签: c++ c++11 vector

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

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

#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:这里直接声明了矢量&#34;内联&#34; (不确定这实际上是如何调用的)在for语句中,但当然每次调用func2时都会构造该向量。

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

5 个答案:

答案 0 :(得分: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

答案 1 :(得分:8)

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

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

答案 2 :(得分: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-statement range_expression 必须先验地初始化,或者必须传入 braced-init-list 进行评估(如std::vector<int>{1, 2, 3, 4})。这将在C ++ 20中更改。

答案 3 :(得分:2)

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

另外,关闭优化反汇编并不是很有意义。

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

答案 4 :(得分: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);
}