我有一个关于编译时函数的问题。我知道 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);
}
我正在玩这个示例并发现 static_assert 不适用于 constinit const 初始化映射。我评论了每一种可能性,我想解释一下。
- Map 被初始化为非常量变量。我知道这不起作用,它不是常量变量,也不是初始化的编译时间
- Map 被初始化为const变量。这也不起作用,即使变量是常量,也不能保证它会在编译时创建。
- Map 被初始化为constexpr变量。这保证了变量将在编译时被初始化。它也意味着常量,所以我们有编译时常量变量。这可以正常工作。(https://en.cppreference.com/w/cpp/language/constexpr)
- Map 被初始化为constinit变量。现在, constinit 保证表达式是零初始化或常量初始化的。我用常量初始化,因此在编译时应该知道这个变量(将静态变量的初始值设置为编译时常量。- https://en.cppreference.com/w/cpp/language/constant_initialization)但是它并不意味着常量,所以我们有编译时非常量变量,这个 static_assert 不能工作。
- Map 被初始化为constinit con t 变量。现在我们有了编译时常量变量,但 static_assert 拒绝工作。static_assert 需要 bool 类型的上下文转换的常量表达式(https://en.cppreference.com/w/cpp/language/static_assert),它是 T 类型的转换常量表达式是隐式转换为 T 类型的表达式,其中转换后的表达式是一个常数表达式。为什么这不起作用?
根据 cppInsights,编译器生成的代码与 constinit const 和 constexpr 相同。