假设我有constexpr
数据集。我主要在编译时使用它,它为可能影响未来constexpr
语句的设置存储数据。string_view
允许我命名这些设置并轻松搜索它们:
struct data {
std::string_view name;
int value;
};
constexpr auto data_set = std::array{
data{"foo", 100},
data{"bar", 200},
};
,这是constexpr
函数,允许我查找数据点并返回它们的值:
constexpr int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
}
当我用正确的字符串名和不正确的字符串名调用这个函数时,工作得很好。因为如果没有找到name
,则该函数没有返回语句,因此无法初始化constexpr
值:
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2"); //Error C2131: expression did not evaluate to a constant
}
和我的IDE (Visual Studio)甚至给了我一个很好的可见的活动错误:
Error (active) E0028 expression must have a constant value
然而,当使用这个并对它进行非constexpr调用时,我总是得到警告
warning C4715: 'get_data': not all control paths return a value
,显然我不能添加一个默认的返回值,因为那会破坏"constexpr
失败,如果invalidate "目的,但我仍然希望能够用它初始化运行时变量(因为搜索string_view在编译时仍然是已知的,使其安全)。
那么是否有一种方法可以保留无效输入的错误,但可以删除错误?对于不返回任何值的函数,也可以使用这种方法吗?
consteval
将启用此行为。这样一来,任何以这种方式标记的函数都必须在编译时求值。这也将删除警告,因为如果它没有正确计算,它将被错误取代。
consteval int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
}
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2");
}
"foo2"
行导致C2131和活动错误E3133:
Error C2131 expression did not evaluate to a constant
Error (active) E3133 call to consteval function "get_data" did not produce a valid constant expression
你仍然可以使用它来初始化运行时变量:
auto bar = get_data("bar");
作为get_data
可以在编译时求值,基本上被data{ "bar", 200 }.value;
或200
取代。
现在consteval
将使纯运行时调用不再工作(如从std::cin
输入或从文件中读取搜索项),但这是期望的行为,因为如果输入无效,理论上可能导致未指定的分支。
你也可以使这个工作的函数返回什么?
可以在被到达的分支中添加无效的语句。例如1 / 0
:
consteval void check_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return;
}
}
auto invalid = 1 / 0;
}
int main() {
check_data("foo");
check_data("bar");
check_data("bar2"); //E3133 active error
}
@GoswinvonBrederlow建议抛出异常,这也工作得很好,在我看来甚至比1 / 0
更干净:
consteval auto get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
throw std::exception("fail");
}
consteval void check_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return;
}
}
throw std::exception("fail");
}
int main() {
constexpr auto foo = get_data("foo");
constexpr auto foo2 = get_data("foo2"); //E3133 active error
auto bar2 = get_data("foo2"); //E3133 active error
check_data("foo");
check_data("foo2"); //E3133 active error
}
我知道可以有许多优雅的解决方案,但我建议的是像在函数末尾添加无条件返回一样简单。
constexpr int get_data(std::string_view search) {
for (auto& data : data_set) {
if (data.name == search) {
return data.value;
}
}
return -1;
}
查看运行情况:https://godbolt.org/z/zKx7M3j8a