如果在 lambda 中使用 static_assert 的 constexpr,哪个编译器是正确的?
if constexpr with static_assert in lambda, which compiler is correct?
当我们想在 if constexpr
中使用 static_assert
时,我们必须使条件依赖于某些模板参数。有趣的是,当代码被包裹在 lambda 中时,gcc 和 clang 不同意。
以下代码使用 gcc 编译,但 clang 触发断言,即使 if constexpr
不可能为真。
#include <utility>
template<typename T> constexpr std::false_type False;
template<typename T>
void foo() {
auto f = [](auto x) {
constexpr int val = decltype(x)::value;
if constexpr(val < 0) {
static_assert(False<T>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
int main() {
foo<int>();
}
将False<T>
替换为False<decltype(x)>
即可轻松修复。
所以问题是:哪个编译器是正确的?我假设 gcc 是正确的,因为 static_assert
中的条件取决于 T
,但我不确定。
这里通常的规则是[temp.res]/8:
The program is ill-formed, no diagnostic required, if: no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated
实例化 foo<T>
后,您拥有的 static_assert
将不再依赖。它变为 static_assert(false)
- 对于通用 lambda f
的调用运算符的所有可能实例化。这是错误的,不需要诊断。 Clang 诊断,gcc 没有。都是正确的。
注意这里的static_assert
被丢弃并不重要。
It can easily be fixed by substituting False<T>
by False<decltype(x)>
.
这使得 static_assert
依赖于通用 lambda,现在我们进入了一个假设可能存在有效特化的状态,因此我们不再是病式的,ndr。
来自 [stmt.if]/2(强调我的)
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity ([temp.pre]), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
读到有人会认为静态断言会被删除,但事实并非如此。
静态断言在模板的第一阶段触发,因为编译器知道它始终为假。
来自 [temp.res]/8(强调我的)
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ]
The program is ill-formed, no diagnostic required, if:
- (8.1) no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[...]
确实如此,您的 False<T>
取决于 T
。问题是通用 lambda 本身就是一个模板,False<T>
不依赖于 lambda 的任何模板参数。
对于 False<T>
为假的 T
,静态断言将始终为假,无论将哪个模板参数发送到 lambda。
编译器可以看到,对于模板的任何实例化 operator()
,静态断言将始终针对当前 T 触发。因此出现编译器错误。
一个解决方案是依赖于x
:
template<typename T>
void foo() {
auto f = [](auto x) {
if constexpr(x < 0) {
static_assert(False<decltype(x)>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
当我们想在 if constexpr
中使用 static_assert
时,我们必须使条件依赖于某些模板参数。有趣的是,当代码被包裹在 lambda 中时,gcc 和 clang 不同意。
以下代码使用 gcc 编译,但 clang 触发断言,即使 if constexpr
不可能为真。
#include <utility>
template<typename T> constexpr std::false_type False;
template<typename T>
void foo() {
auto f = [](auto x) {
constexpr int val = decltype(x)::value;
if constexpr(val < 0) {
static_assert(False<T>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
int main() {
foo<int>();
}
将False<T>
替换为False<decltype(x)>
即可轻松修复。
所以问题是:哪个编译器是正确的?我假设 gcc 是正确的,因为 static_assert
中的条件取决于 T
,但我不确定。
这里通常的规则是[temp.res]/8:
The program is ill-formed, no diagnostic required, if: no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated
实例化 foo<T>
后,您拥有的 static_assert
将不再依赖。它变为 static_assert(false)
- 对于通用 lambda f
的调用运算符的所有可能实例化。这是错误的,不需要诊断。 Clang 诊断,gcc 没有。都是正确的。
注意这里的static_assert
被丢弃并不重要。
It can easily be fixed by substituting
False<T>
byFalse<decltype(x)>
.
这使得 static_assert
依赖于通用 lambda,现在我们进入了一个假设可能存在有效特化的状态,因此我们不再是病式的,ndr。
来自 [stmt.if]/2(强调我的)
If the if statement is of the form if constexpr, the value of the condition shall be a contextually converted constant expression of type bool; this form is called a constexpr if statement. If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity ([temp.pre]), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated.
读到有人会认为静态断言会被删除,但事实并非如此。
静态断言在模板的第一阶段触发,因为编译器知道它始终为假。
来自 [temp.res]/8(强调我的)
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
- (8.1) no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[...]
确实如此,您的 False<T>
取决于 T
。问题是通用 lambda 本身就是一个模板,False<T>
不依赖于 lambda 的任何模板参数。
对于 False<T>
为假的 T
,静态断言将始终为假,无论将哪个模板参数发送到 lambda。
编译器可以看到,对于模板的任何实例化 operator()
,静态断言将始终针对当前 T 触发。因此出现编译器错误。
一个解决方案是依赖于x
:
template<typename T>
void foo() {
auto f = [](auto x) {
if constexpr(x < 0) {
static_assert(False<decltype(x)>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}