constexpr guard 子句不编译

constexpr guard clause does not compile

提问人:Fredrik Enetorp 提问时间:6/30/2023 最后编辑:Fredrik Enetorp 更新时间:6/30/2023 访问量:97

问:

我想在我的代码中添加一个 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 子句来跳过不支持该操作的类型。

C++ 模板 if-constexpr guard-clause

评论

2赞 273K 6/30/2023
第二个不是语句的一部分,因此不会被丢弃。++t;if consexprstd::string
5赞 n. m. could be an AI 6/30/2023
你会希望编译吗?std::string s; return; ++s;
1赞 Drew Dormann 6/30/2023
@FredrikEnetorp你的问题似乎是“为什么这行不通?它不起作用,因为不在任何 guard 子句中。无论 的类型如何,它都会被编译。你编译的例子确实有一个 guard 子句。++tt++t
1赞 Fredrik Enetorp 6/30/2023
@DrewDormann 守卫子句的要点是提前退出,因为它是 constexpr,因此如果子句触发,编译器丢弃其余部分是有意义的。是的,这显然不是这样,但有充分的理由说明为什么不呢?
1赞 PaulMcKenzie 6/30/2023
但是有充分的理由说明为什么不呢?-- 原因是C++不是这样工作的。将编译一段不受保护的代码。

答:

3赞 user17732522 6/30/2023 #1

采用并无条件退出函数的分支不会阻止对语句后面替换表达式的有效性进行检查,原因与简单的无条件语句不会阻止相同的检查相同。if constexprif constexprreturn

当然,从理论上讲,如果有一个语句无条件地退出一个函数,那么人们可以争辩说,该语句之后的内容并不重要,而模板参数的替换是否会导致有效的表达式也无关紧要。

但是,没有这样的规则。无条件退出后的语句在替换后仍会检查其有效性,就像检查任何其他未丢弃的语句一样。

如果有这样的规则,它需要准确定义何时不检查语句的有效性。一般来说,函数中的语句是否可访问是无法确定的(在可计算性理论的意义上)。因此,充其量需要指定特殊情况,例如同一作用域中的单独语句或嵌套分支作为应用特殊规则的条件。returnif constexpr

但是,根据函数编写的确切形式,您会有一个奇怪的差异。我不认为增加这种复杂性总体上是有益的。此外,即使无法访问替换语句,也会产生其他影响,例如,关于模板专用化的 odr 使用和隐式实例化,这些语句也会根据函数体的确切语法形式而具有不同的行为。

也不需要这样的规则,因为它在实践中总是可以通过 .在你的例子中,条件可以被否定,然后可以放在 true-branch 中,以稍微简化语法。否则,可以采取分支。这需要一些额外的标点符号和缩进,这不太可能成为增加语言复杂性的充分理由。无论如何,C++在语法上都不是很简洁。if constexpr++telse