如果constexpr在lambda中带有static_assert,哪个编译器正确?


13

当我们要在中使用时static_assertif constexpr必须使条件取决于某些模板参数。有趣的是,当代码包装在lambda中时,gcc和clang不同意。

以下代码使用gcc进行编译,但是clang触发断言,即使if constexpr不能为true。

#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,但是我不确定。


这会问同样的问题,但方向相反:stackoverflow.com/questions/59393908/…
NathanOliver

1
@mfnx 无法复制。你可以分享一个例子吗?
NathanOliver

2
我会说两者都是正确的(格式错误的NDR):static_assert(False<int>, "AAA");等同于static_assert(false, "AAA");lambda内部。
Jarod42

2
@mfnx您已经更改了常量的值。使用OP的常量为f(std::integral_constant<int, 1>{});Wandbox 的示例不会触发断言:wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliver是的,您是对的,抱歉。似乎gcc是正确的,因为如果常量> = 0,则应该丢弃constexpr中的代码;
mfnx

Answers:


1

[stmt.if] / 2(强调我)开始

如果if语句的形式为if constexpr,则条件的值应为bool类型的上下文转换常量表达式;这种形式称为constexpr if语句。如果转换后的条件的值为false,则第一个子语句为废弃的语句,否则,第二个子语句(如果存在)为废弃的语句。在封装模板化实体([temp.pre])的实例化过程中,如果条件在实例化后不依赖于值,则不会实例化丢弃的子语句(如果有)。

读到有人会认为静态断言会被丢弃,但事实并非如此。

静态断言在模板的第一阶段中触发,因为编译器知道它始终为false。

[temp.res] / 8(强调我的)开始

可以在任何实例化之前检查模板的有效性。[ 注意:知道哪些名称是类型名称,这样就可以检查每个模板的语法。— 结束说明 ]该程序格式错误,如果满足以下条件,则无需诊断:

  • (8.1)如果模板中的语句或constexpr的子语句未实例化则无法为该模板或constexpr的子语句生成有效的专业化名称;或者

[...]

是的,的确False<T>取决于您T。问题在于,通用lambda本身就是模板,并且False<T>不依赖于lambda的任何模板参数。

对于TFalse<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>{});
}

现场例子


13

这里的通常规则是[temp.res] / 8

该程序格式不正确,无需进行诊断,如果:如果没有在模板中声明并且模板未实例化,则无法为模板或constexpr的子语句生成有效的专业化名称

实例化之后foo<T>static_assert您所拥有的不再依赖。static_assert(false)对于通用lambda的call运算符的所有可能实例化,它都变为f。格式错误,无需诊断。lang诊断,gcc不能。两者都是正确的。

请注意,static_assert此处丢弃无关紧要。

它可以很容易地通过用固定False<T>通过False<decltype(x)>

这将static_assert依赖项保留在通用lambda中,现在我们进入了一种状态,在该状态下可以假设存在有效的专业化,因此我们不再是格式错误的ndr。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.