C++ &引用;内联“;具有初始值设定项列表的对象的静态声明
对不起,这个标题太难看了,但我找不到更好的了 考虑此示例代码(除了说明问题外,它没有任何用途):C++ &引用;内联“;具有初始值设定项列表的对象的静态声明,c++,c++11,vector,C++,C++11,Vector,对不起,这个标题太难看了,但我找不到更好的了 考虑此示例代码(除了说明问题外,它没有任何用途): #包括 void FooBar(int); void func1() { 静态std::向量向量向量{1,2,3,4}; 用于(自动&v:vec) 福巴(五); } void func2() { for(auto&v:std::vector{1,2,3,4}) 福巴(五); } 可在 在func1中,静态vec向量应在启动时一次性构建。实际上,上面提到的godbolt上的反汇编表明,静态vec的初
#包括
void FooBar(int);
void func1()
{
静态std::向量向量向量{1,2,3,4};
用于(自动&v:vec)
福巴(五);
}
void func2()
{
for(auto&v:std::vector{1,2,3,4})
福巴(五);
}
可在
在func1
中,静态vec
向量应在启动时一次性构建。实际上,上面提到的godbolt上的反汇编表明,静态vec
的初始化只在第一次调用func1
时完成,而不是在启动时完成,但这不是重点
现在考虑<代码>函数2> /COD>:这里的向量在<<代码> > < /C> >语句中直接声明为“内联”(不确定这实际上是怎么调用的),但是当然,每次代码<函数2 > /COD>被调用时,都会构建向量。
< P>有一种方法可以静态地声明向量,在<代码> > < /Cord>语句中,比如<代码>(Auto& V:static STD::vector { 1, 2, 3,4 })< /COMP> >,不幸的是,这不是合法的C++。 < P>这对您实际上并没有帮助。但是在C++2a中可以做一些事情。然而,它是基于C++14和C++17中已经存在的东西 C++2a在基于范围的for循环中添加了一个
init语句
。这是新的一位,旧的一位是它与今天定义的init语句相同。定义如下():
我要说的是简单声明
可能包含静态
限定符。是的,它能满足你的期望。因此,在C++2a中,您可以编写:
for (static std::vector<int> vec {1, 2, 3, 4}; int v : vec) {
// Do things.
}
for(静态std::vector vec{1,2,3,4};int v:vec){
//做事。
}
如果您现在想测试编译器对它的支持,这里有一个C++17难题:
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);
}
if(静态std::向量向量{1,2,3,4};true){
//if中的init语句是在C++17中添加的
用于(INTV:vec)
福巴(五);
}
<>(P/>>P>不),C++中的C++(17)是不可能的。基于范围的等效于以下伪代码:
{
auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
这里没有init语句。range_表达式必须预先初始化,或者必须传入带括号的init列表以进行计算(如std::vector{1,2,3,4}
)。这将在C++20中更改。另一个使用lambda的选项(来自对主要问题的评论):
void func()
{
for(auto&v:([]()->std::vector&{static std::vector vec{1,2,3,4};return vec;})()
福巴(五);
}
>P>确实,C++标准中没有任何东西禁止您正在寻找的优化。std::vector
分配的堆内存实际上可以被静态内存替换,而无需更改程序行为。但正如您的链接所示(即使添加了积极的优化选项),编译器也不会执行预期的优化
因此,您可以选择使用std::array
而不是std::vector
,std::array
很容易被优化器“理解”,优化器包括:
void FooBar(int);
void func2()
{
for (auto & v : std::array<int,4> {1, 2, 3, 4})
FooBar(v);
}
为了好玩,您可以使用使用静态内存的自定义分配器获得同样好的程序集,包括:
void FooBar(inti);
模板
类静态分配
{
静态typename std::aligned_storage::type buff;
静态布尔分配;
公众:
使用值_type=T;
布尔运算符==(常量静态分配&){
返回true;
}
布尔运算符!=(常量静态变量&){
返回false;
}
T*分配(标准::大小\u T n)
{
如果(已分配)抛出std::bad_alloc{};
分配=真;
返回重新解释施法(&buff);
}
无效解除分配(T*,std::size\u T)
{
分配=假;
}
};
模板
bool static_alloc::allocated=false;
模板
std::aligned\u storage\u t static\u alloc::buff;
void func2()
{
for(auto&v:std::vector{1,2,3,4})
福巴(五);
}
你可以做得更好:你可以把所有东西都放在堆栈上。如果您在-O2或更高版本上编译以下代码,它基本上会展开为对FooBar()
的4个调用
此外,在关闭优化的情况下查看反汇编也没有什么意义
void func3()
{
constexpr int vs [] = { 1, 2, 3, 4 };
for ( int const v : vs )
FooBar(v);
}
也许在自定义类中包装它?再看看这个@JakeFreeman,但这会使代码比func1
中使用的方法更麻烦。除了好奇因素,第二种方法还有什么好处吗?注意,在这里使用std::vector
会适得其反,因为它的全部目的是运行时的可调整性,这与您想要一个单例/常量的愿望背道而驰。因此,我假设这是某个用户类的占位符,其中constepr
版本(std::array
或std::initializer\u list
)不可用。@MichaelWalz如果不想考虑大小,可以经常调用或使用类似的函数。哈,我不知道这里可以包括static
(从来没有任何理由尝试)。if
版本是否比仅仅打开一个新的块作用域有什么好处?@下划线\u d-没有。这只是对语法结果的测试。演示静态对象的init语句是否有效。Yepp。编译并运行:但它看起来仍然很可怕。(我印象深刻-没有掌握operator()
的诀窍)出于好奇:为什么它可以与operator()
一起工作,但在没有@Scheff的情况下甚至不能编译?@Scheff-通过函数调用,它调用lambda并获取要迭代的向量。在没有调用的情况下,它尝试在lambda对象本身上迭代。为了自动进行所讨论的优化,编译器必须认识到,即使程序对ea采用非const
左值引用,也不会写入元素
void func()
{
for(auto & v : ([]() -> std::vector<int>& { static std::vector<int> vec{1, 2, 3, 4}; return vec; })())
FooBar(v);
}
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
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);
}
void func3()
{
constexpr int vs [] = { 1, 2, 3, 4 };
for ( int const v : vs )
FooBar(v);
}