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);
}