使用 constinit const 时 static_assert 失败。constinit、constinit const、constexpr、const、非常量变量中的混淆

How*_*owP 0 c++ constexpr c++20 constinit

我有一个关于编译时函数的问题。我知道 static_assert 应该只适用于可以在编译时评估/计算的类型。所以它不适用于 std::string (然而,gcc10 中不支持 constexpr std::string)但可以使用 std::array(当我在编译时知道大小时)。我正在观看 Jason Turner 的 C++ Weekly,所以这个片段来自这一集https://www.youtube.com/watch?v=INn3xa4pMfg

代码在这里:https : //godbolt.org/z/e3WPTP

#include <array>
#include <algorithm>

template<typename Key, typename Value, std::size_t Size>
struct Map final
{
    std::array<std::pair<Key, Value>, Size> _data;

    [[nodiscard]] constexpr Value getMappedKey(const Key& aKey) const
    {
        const auto mapIterator = std::ranges::find_if(_data, [&aKey](const auto& pair){ return pair.first == aKey;});

        if(mapIterator != _data.end())
        {
            return mapIterator->second;
        }
        else
        {
            throw std::out_of_range("Key is not in the map");
        }
    }
};

enum class OurEnum
{
    OUR_VALUE,
    OUR_VALUE2,
    OUR_VALUE3
};

enum class TheirEnum
{
    THEIR_VALUE,
    THEIR_VALUE2,
    THEIR_VALUE3
};


// This Fails non constant variable of course
/*
    Map<OurEnum, TheirEnum, 2> enumsConverter =
    {
        {
            {{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
            {OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
        }
    };
*/

// This fails, it is const, but this does not guarentee that it will be created in compile time
/*
    const Map<OurEnum, TheirEnum, 2> enumsConverter =
    {
        {
            {{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
            {OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
        }
    };
*/

// This works
/*
    constexpr Map<OurEnum, TheirEnum, 2> enumsConverter =
    {
        {
            {{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
            {OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
        }
    };
*/

//How come this does not work? Oh i see, missing const because constinit does not apply constness
/*
    constinit Map<OurEnum, TheirEnum, 2> enumsConverter =
    {
        {
            {{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
            {OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
        }
    };
*/

// Okay, I added const specifier but still this makes static_assert fail because of non-constant condition
// Why?

    constinit const Map<OurEnum, TheirEnum, 2> enumsConverter =
    {
        {
            {{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
            {OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
        }
    };


int main() 
{
    static_assert(enumsConverter.getMappedKey(OurEnum::OUR_VALUE) == TheirEnum::THEIR_VALUE);
}
Run Code Online (Sandbox Code Playgroud)

我在玩这个示例并发现 static_assert 不适用于 constinit const 初始化映射。我注释掉了所有可能性,我想解释一下。

  1. Map 被初始化为非常量变量。我知道这行不通,它不是常量变量,也不是编译时初始化
  2. Map 被初始化为const变量。这也不起作用,即使变量是常量,也不能保证它会在编译时创建。
  3. Map 被初始化为constexpr变量。这保证将在编译时初始化变量。它也意味着常量性,所以我们有编译时常量变量。这工作正常。( https://en.cppreference.com/w/cpp/language/constexpr )
  4. Map 被初始化为constinit变量。现在, constinit 保证表达式是零初始化或常量初始化。我用常量初始化,所以根据这个变量应该在编译时知道(将静态变量的初始值设置为编译时常量。 - https://en.cppreference.com/w/cpp/language/constant_initialization)但是它并不意味着常量,所以我们有编译时非常量变量,这个 static_assert 不能工作。
  5. Map 被初始化为constinit cons t 变量。现在我们有了编译时常量变量,但 static_assert 拒绝工作。static_assert 需要上下文转换的 bool 类型常量表达式 ( https://en.cppreference.com/w/cpp/language/static_assert ),即 T 类型的转换常量表达式是隐式转换为 T 类型的表达式,其中转换后的表达式是一个常量表达式。为什么这不起作用?

根据 cppInsights,编译器生成的代码与 constinit const 和 constexpr 相同。

Bar*_*rry 7

Map 被初始化为constinit const变量。现在我们有编译时常量变量但static_assert拒绝工作

第二个主张不是从第一个主张而来的。我们没有编译时常量变量,因此static_assert不起作用。

constinit不会使您的变量成为constexpr变量,它仅保证您具有常量初始化(因此名称为constinit)。事实上,constinit甚至不暗示const

constinit std::mutex m;
Run Code Online (Sandbox Code Playgroud)

是一个有效且激励性的用途constinit,并且仍然允许我锁定和解锁m

拥有constexpr变量的唯一方法是声明您的变量constexpr(不幸的是,对于声明的整型类型const,这并不适用)。如果你想要一张constexpr地图,你需要声明你的地图constexpr