[英]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 :
程序格式错误,无需诊断,如果: 不能为模板或 constexpr 的子语句生成有效的特化 模板中的 if 语句且模板未实例化
实例化foo<T>
,您拥有的static_assert
不再依赖。 它变为static_assert(false)
- 对于泛型 lambda f
的调用运算符的所有可能实例化。 这是格式错误的,不需要诊断。 Clang 诊断,gcc 没有。 两者都是正确的。
请注意,这里的static_assert
被丢弃并不重要。
通过将
False<T>
替换为False<decltype(x)>
可以轻松修复它。
这使static_assert
依赖于泛型 lambda,现在我们进入一种状态,假设可能存在有效的特化,因此我们不再是格式错误的,ndr。
来自[stmt.if]/2 (强调我的)
如果 if 语句的形式为 if constexpr,则条件的值应为上下文转换的 bool 类型常量表达式; 这种形式称为 constexpr if 语句。 如果转换条件的值为假,则第一个子语句是丢弃的语句,否则第二个子语句(如果存在)是丢弃的语句。 在封闭模板化实体 ([temp.pre]) 的实例化期间,如果条件在实例化后不依赖于值,则不会实例化丢弃的子语句(如果有)。
读到人们会认为静态断言会被删除,但事实并非如此。
静态断言在模板的第一阶段被触发,因为编译器知道它总是假的。
来自[temp.res]/8 (强调我的)
可以在任何实例化之前检查模板的有效性。 [注意:知道哪些名称是类型名称允许以这种方式检查每个模板的语法。 —尾注] 程序格式错误,无需诊断,如果:
- (8.1) 不能为模板或模板内的 constexpr if 语句的子语句生成有效的特化,并且模板没有被实例化,或者
[...]
是的,您的False<T>
取决于T
。 问题在于泛型 lambda 本身就是一个模板,并且False<T>
不依赖于 lambda 的任何模板参数。
对于一个T
的是False<T>
是假的,静态断言永远是假的,无论哪个模板参数发送到拉姆达。
编译器可以看到,对于模板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>{});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.