提问人:Fredrik Enetorp 提问时间:6/30/2023 最后编辑:Fredrik Enetorp 更新时间:6/30/2023 访问量:97
constexpr guard 子句不编译
constexpr guard clause does not compile
问:
我想在我的代码中添加一个 constexpr guard 子句以避免不必要的缩进,但遇到了这个问题。
这将编译:
#include <string>
#include <iostream>
template<class T>
void inc(T& t) {
if constexpr (!std::is_arithmetic_v<T>) {
return;
} else {
++t;
}
}
int main() {
int i = 1;
std::string s = "bar";
inc(i);
inc(s);
std::cout << "Success!";
}
而这不会:
#include <string>
#include <iostream>
template<class T>
void inc(T& t) {
if constexpr (!std::is_arithmetic_v<T>) {
return;
}
++t;
}
int main() {
int i = 1;
std::string s = "bar";
inc(i);
inc(s);
std::cout << "Success!";
}
编译器错误:
main.cpp:9:5: error: cannot increment value of type 'std::string'
++t;
^ ~
main.cpp:17:5: note: in instantiation of function template specialization 'inc<std::string>' requested here
inc(s);
^
1 error generated.
为什么这行不通?
澄清一下:我正在编写一个 googletest TYPED_TEST来测试我的代码是否适合各种类型,但有些测试要求该类型是可递增的。我想添加一个 constexpr guard 子句来跳过不支持该操作的类型。
答:
采用并无条件退出函数的分支不会阻止对语句后面替换表达式的有效性进行检查,原因与简单的无条件语句不会阻止相同的检查相同。if constexpr
if constexpr
return
当然,从理论上讲,如果有一个语句无条件地退出一个函数,那么人们可以争辩说,该语句之后的内容并不重要,而模板参数的替换是否会导致有效的表达式也无关紧要。
但是,没有这样的规则。无条件退出后的语句在替换后仍会检查其有效性,就像检查任何其他未丢弃的语句一样。
如果有这样的规则,它需要准确定义何时不检查语句的有效性。一般来说,函数中的语句是否可访问是无法确定的(在可计算性理论的意义上)。因此,充其量需要指定特殊情况,例如同一作用域中的单独语句或嵌套分支作为应用特殊规则的条件。return
if constexpr
但是,根据函数编写的确切形式,您会有一个奇怪的差异。我不认为增加这种复杂性总体上是有益的。此外,即使无法访问替换语句,也会产生其他影响,例如,关于模板专用化的 odr 使用和隐式实例化,这些语句也会根据函数体的确切语法形式而具有不同的行为。
也不需要这样的规则,因为它在实践中总是可以通过 .在你的例子中,条件可以被否定,然后可以放在 true-branch 中,以稍微简化语法。否则,可以采取分支。这需要一些额外的标点符号和缩进,这不太可能成为增加语言复杂性的充分理由。无论如何,C++在语法上都不是很简洁。if constexpr
++t
else
评论
++t;
if consexpr
std::string
std::string s; return; ++s;
++t
t
++t