gpt4 book ai didi

c++ - constexpr函数中的静态constexpr变量

转载 作者:行者123 更新时间:2023-12-01 12:16:40 27 4
gpt4 key购买 nike

static函数内部不允许使用constexpr变量。这是有道理的,因为static将向假定为纯函数的状态引入状态。

但是,我不明白为什么我们在static constexpr函数中不能有constexpr变量。确保始终具有相同的值,因此该函数将保持纯净。

我为什么要在乎?因为static在运行时会有所不同。考虑以下代码:

#include <array>

constexpr int at(const std::array<int, 100>& v, int index)
{
return v[index];
}

int foo1(int i) {
static constexpr std::array<int, 100> v = {
5, 7, 0, 0, 5 // The rest are zero
};
return at(v, i);
}

constexpr int foo2(int i) {
constexpr std::array<int, 100> v = {
5, 7, 0, 0, 5 // The rest are zero
};
return at(v, i);
}

int foo2_caller(int i) {
return foo2(i);
}

现场直播: https://gcc.godbolt.org/z/umdXgv
foo1具有3条asm指令,因为它将缓冲区存储在静态存储器中。 foo2具有15条asm指令,因为需要在每次调用时分配和初始化缓冲区,并且编译器无法对其进行优化。

请注意,此处 foo1仅用于显示 foo2中的缺陷。我想编写一个在编译和运行时都可以使用的函数。这就是 foo2背后的想法。但是我们看到它不能像仅运行时的 foo1那样高效,这令人不安。

我找到了唯一有意义的相关讨论 is this,但它没有专门讨论 static constexpr

问题是:
  • 我的推理正确吗,还是我错过了static constexpr变量可能引起的一些问题?
  • 是否有解决此问题的建议?
  • 最佳答案

    Is my reasoning correct, or do I miss some problem that static constexpr variables might cause?


    如果在 constexpr上下文中允许静态存储持续时间,则在处理 constexpr变量时,有一些边缘情况需要考虑静态存储持续时间。
    函数中具有静态存储持续时间的对象仅在首次进入函数时构造。通常,这时通常将存储支持应用于常量(对于运行时常量)。如果在 static constexpr上下文中允许使用 constexpr,则在编译时生成该代码时,必须发生以下两种情况之一:
  • 现在,在编译时执行该函数必须为静态常量生成存储支持,以防万一使用ODR -即使在运行时从未使用过该函数(这将是非零开销),或者是
  • 现在,在编译时执行该函数必须立即创建一个常量,该常量将在每次调用时实例化,并最终在分支使用运行时上下文调用该常量时给定存储空间(无论是否在编译时生成)。这会违反静态存储持续时间对象的现有规则。

  • 由于 constexpr在整个上下文中本质上是无状态的,因此在 constexpr函数调用期间应用静态存储对象会突然在 constexpr调用之间添加状态-对于 constexpr的当前规则而言,这是一个很大的变化。尽管 constexpr函数可以修改本地状态,但该状态不会受到全局影响。
    C++ 20还放宽了 constexpr的要求,以允许析构函数成为constexpr,这引发了更多问题,例如在上述情况下何时必须执行析构函数。
    我并不是说这不是一个可以解决的问题;只是现有的语言设施使解决这个问题变得有些复杂,而又没有违反某些规则。
    使用自动存储持续时间对象,这很容易推断-因为存储是在特定的时间点连贯创建和销毁的。

    Are there any proposals to fix this?


    我所不知道的。各个Google小组都在讨论有关它的规则,但是我还没有看到关于它的任何建议。如果有人知道,请在评论中将其链接,然后我将更新我的答案。
    解决方法
    您可以通过几种方法来避免此限制,具体取决于您所需的API是什么,以及要求是什么:
  • 将该常量放入文件范围,也许在detail命名空间下。这使您始终处于全局状态,这可能无法满足您的要求。
  • 将常量放入static/struct中的class常量中。如果需要对数据进行模板化,则可以使用此方法,并允许您使用privatefriend船来控制对此常量的访问。
  • 使该函数成为包含数据的static/struct上的class函数(如果这符合您的要求)。

  • 如果方法1仅适用于C++ 14(C++ 11没有可变模板),则尽管方法1仅适用于C++ 14,但所有这三种方法都可以很好地工作,而方法2和3可以在C++中使用。 11。
    我认为,封装方面最干净的解决方案是将数据和作用函数都移到 structclass中的第三种方法。这样可以使数据与功能紧密相关。例如:
    class foo_util
    {
    public:
    static constexpr int foo(int i); // calls at(v, i);
    private:
    static constexpr std::array<int, 100> v = { ... };
    };
    Compiler Explorer Link
    这将生成与 foo1方法相同的程序集,同时仍允许它成为 constexpr

    如果无法根据您的要求将函数扔到 classstruct中(也许这需要是一个自由函数?),那么您就不得不将数据移到文件范围(也许受 detail命名空间约定保护) ,或将其放入处理数据的不连续的 struct class中。后一种方法可以使用访问修饰符和友元来控制数据访问。此解决方案可以工作,尽管它显然不是那么干净:
    #include <array>

    constexpr int at(const std::array<int, 100>& v, int index)
    {
    return v[index];
    }

    constexpr int foo(int i);
    namespace detail {
    class foo_holder
    {
    private:
    static constexpr std::array<int, 100> v = {
    5, 7, 0, 0, 5 // The rest are zero
    };
    friend constexpr int ::foo(int i);
    };
    } // namespace detail

    constexpr int foo(int i) {
    return at(detail::foo_holder::v, i);
    }
    Compiler Explorer Link
    同样,这会产生与 foo1相同的程序集,同时仍然允许它成为 constexpr

    关于c++ - constexpr函数中的静态constexpr变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62458079/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com