很抱歉这个尴尬的标题,但我找不到更好的标题。
考虑这个示例代码(除了说明问题之外没有任何目的):
#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 ++。
答案 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);
}
答案 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::vector
,std::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);
}