C++ 函数内的静态 constexpr 变量有意义吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13865842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:46:29  来源:igfitidea点击:

Does static constexpr variable inside a function make sense?

c++staticc++11constexpr

提问by David Stone

If I have a variable inside a function (say, a large array), does it make sense to declare it both staticand constexpr? constexprguarantees that the array is created at compile time, so would the staticbe useless?

如果我在一个函数中有一个变量(比如一个大数组),那么同时声明它staticconstexpr是否有意义?constexpr保证数组是在编译时创建的,所以static没用吗?

void f() {
    static constexpr int x [] = {
        // a few thousand elements
    };
    // do something with the array
}

Is the staticactually doing anything there in terms of generated code or semantics?

static就生成的代码或语义而言,实际上是否在做任何事情?

回答by rici

The short answer is that not only is staticuseful, it is pretty well always going to be desired.

简短的回答是static,它不仅有用,而且总是很受欢迎。

First, note that staticand constexprare completely independent of each other. staticdefines the object's lifetime during execution; constexprspecifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space. So once the program is compiled, constexpris no longer relevant.

首先,注意staticconstexpr是完全独立的。static定义对象在执行期间的生命周期;constexpr指定对象在编译期间应该可用。编译和执行在时间和空间上都是不相交和不连续的。所以一旦程序被编译,constexpr就不再相关了。

Every variable declared constexpris implicitly constbut constand staticare almost orthogonal (except for the interaction with static constintegers.)

声明的每个变量constexpr是含蓄const,但conststatic几乎垂直(除了与交互static const整数)。

The C++object model (§1.9) requires that all objects other than bit-fields occupy at least one byte of memory and have addresses; furthermore all such objects observable in a program at a given moment must have distinct addresses (paragraph 6). This does not quite require the compiler to create a new array on the stack for every invocation of a function with a local non-static const array, because the compiler could take refuge in the as-ifprinciple provided it can prove that no other such object can be observed.

所述C++对象模型(§1.9)要求所有对象以外位字段占据的存储器的至少一个字节,并具有地址; 此外,在给定时刻在程序中可观察到的所有此类对象必须具有不同的地址(第 6 段)。这并不完全要求编译器在每次调用具有本地非静态 const 数组的函数时都在堆栈上创建一个新数组,因为编译器可以根据该as-if原则进行避难,前提是它可以证明没有其他此类对象可以观察到的。

That's not going to be easy to prove, unfortunately, unless the function is trivial (for example, it does not call any other function whose body is not visible within the translation unit) because arrays, more or less by definition, are addresses. So in most cases, the non-static const(expr)array will have to be recreated on the stack at every invocation, which defeats the point of being able to compute it at compile time.

不幸的是,这并不容易证明,除非该函数是微不足道的(例如,它不调用其主体在翻译单元中不可见的任何其他函数),因为根据定义,数组或多或少是地址。因此,在大多数情况下,const(expr)每次调用时都必须在堆栈上重新创建非静态数组,这违背了能够在编译时计算它的意义。

On the other hand, a local static constobject is shared by all observers, and furthermore may be initialized even if the function it is defined in is never called. So none of the above applies, and a compiler is free not only to generate only a single instance of it; it is free to generate a single instance of it in read-only storage.

另一方面,局部static const对象被所有观察者共享,而且即使定义它的函数从未被调用,也可能被初始化。所以以上都不适用,编译器不仅可以自由地生成它的单个实例;可以在只读存储中免费生成它的单个实例。

So you should definitely use static constexprin your example.

所以你绝对应该static constexpr在你的例子中使用。

However, there is one case where you wouldn't want to use static constexpr. Unless a constexprdeclared object is either ODR-usedor declared static, the compiler is free to not include it at all. That's pretty useful, because it allows the use of compile-time temporary constexprarrays without polluting the compiled program with unnecessary bytes. In that case, you would clearly not want to use static, since staticis likely to force the object to exist at runtime.

但是,在一种情况下您不想使用static constexpr. 除非已constexpr声明的对象是ODR 使用的或已声明的static,否则编译器可以完全不包含它。这非常有用,因为它允许使用编译时临时constexpr数组,而不会用不必要的字节污染已编译的程序。在这种情况下,您显然不想使用static,因为static很可能会强制对象在运行时存在。

回答by metablaster

In addition to given answer, it's worth noting that compiler is not required to initialize constexprvariable at compile time, knowing that the difference between constexprand static constexpris that to use static constexpryou ensure the variable is initialized only once.

除了给出的答案之外,值得注意的是,编译器不需要constexpr在编译时初始化变量,知道constexpr和之间的区别在于static constexpr使用static constexpr您确保变量只初始化一次。

Following code demonstrates how constexprvariable is initialized multiple times (with same value though), while static constexpris surely initialized only once.

以下代码演示了如何constexpr多次初始化变量(尽管具有相同的值),而static constexpr肯定只初始化一次。

In addition the code compares the advantage of constexpragainst constin combination with static.

此外,代码比较了constexpragainstconststatic.

#include <iostream>
#include <string>
#include <cassert>
#include <sstream>

const short const_short = 0;
constexpr short constexpr_short = 0;

// print only last 3 address value numbers
const short addr_offset = 3;

// This function will print name, value and address for given parameter
void print_properties(std::string ref_name, const short* param, short offset)
{
    // determine initial size of strings
    std::string title = "value \ address of ";
    const size_t ref_size = ref_name.size();
    const size_t title_size = title.size();
    assert(title_size > ref_size);

    // create title (resize)
    title.append(ref_name);
    title.append(" is ");
    title.append(title_size - ref_size, ' ');

    // extract last 'offset' values from address
    std::stringstream addr;
    addr << param;
    const std::string addr_str = addr.str();
    const size_t addr_size = addr_str.size();
    assert(addr_size - offset > 0);

    // print title / ref value / address at offset
    std::cout << title << *param << " " << addr_str.substr(addr_size - offset) << std::endl;
}

// here we test initialization of const variable (runtime)
void const_value(const short counter)
{
    static short temp = const_short;
    const short const_var = ++temp;
    print_properties("const", &const_var, addr_offset);

    if (counter)
        const_value(counter - 1);
}

// here we test initialization of static variable (runtime)
void static_value(const short counter)
{
    static short temp = const_short;
    static short static_var = ++temp;
    print_properties("static", &static_var, addr_offset);

    if (counter)
        static_value(counter - 1);
}

// here we test initialization of static const variable (runtime)
void static_const_value(const short counter)
{
    static short temp = const_short;
    static const short static_var = ++temp;
    print_properties("static const", &static_var, addr_offset);

    if (counter)
        static_const_value(counter - 1);
}

// here we test initialization of constexpr variable (compile time)
void constexpr_value(const short counter)
{
    constexpr short constexpr_var = constexpr_short;
    print_properties("constexpr", &constexpr_var, addr_offset);

    if (counter)
        constexpr_value(counter - 1);
}

// here we test initialization of static constexpr variable (compile time)
void static_constexpr_value(const short counter)
{
    static constexpr short static_constexpr_var = constexpr_short;
    print_properties("static constexpr", &static_constexpr_var, addr_offset);

    if (counter)
        static_constexpr_value(counter - 1);
}

// final test call this method from main()
void test_static_const()
{
    constexpr short counter = 2;

    const_value(counter);
    std::cout << std::endl;

    static_value(counter);
    std::cout << std::endl;

    static_const_value(counter);
    std::cout << std::endl;

    constexpr_value(counter);
    std::cout << std::endl;

    static_constexpr_value(counter);
    std::cout << std::endl;
}

Possible program output:

可能的程序输出:

value \ address of const is               1 564
value \ address of const is               2 3D4
value \ address of const is               3 244

value \ address of static is              1 C58
value \ address of static is              1 C58
value \ address of static is              1 C58

value \ address of static const is        1 C64
value \ address of static const is        1 C64
value \ address of static const is        1 C64

value \ address of constexpr is           0 564
value \ address of constexpr is           0 3D4
value \ address of constexpr is           0 244

value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0

As you can see yourself constexpris initilized multiple times (address is not the same) while statickeyword ensures that initialization is performed only once.

正如你所看到的,你自己constexprstatic初始化了多次(地址不一样),而关键字确保初始化只执行一次。