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

flo*_*tan 13 c++ templates static-assert language-lawyer c++17

当我们想在 astatic_assert中使用 a 时,if constexpr我们必须使条件依赖于某个模板参数。有趣的是,当代码包含在 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>();
}
Run Code Online (Sandbox Code Playgroud)

现场示例在这里

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

所以问题是:哪个编译器是正确的?我认为 gcc 是正确的,因为 中的条件static_assert取决于T,但我不确定。

Bar*_*rry 13

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

程序格式错误,无需诊断,如果: 无法为模板或 constexpr 的子语句生成有效的特化,如果模板中的语句和模板未实例化

实例化后foo<T>static_assert您拥有的不再依赖。它变成static_assert(false)- 对于泛型 lambda 的调用运算符的所有可能实例化f。这是格式错误的,不需要诊断。Clang 诊断,gcc 没有。两者都是正确的。

请注意,它并不重要的是,static_assert这里丢弃。

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

这将static_assert依赖项保留在泛型 lambda 中,现在我们进入一种状态,假设可能存在有效的特化,因此我们不再是格式错误的,ndr。


Gui*_*cot 1

来自[stmt.if]/2(强调我的)

\n\n
\n

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

\n
\n\n

读到这一点,人们会认为静态断言将被删除,但事实并非如此。

\n\n

静态断言在模板的第一阶段被触发,因为编译器知道它总是错误的。

\n\n

来自[temp.res]/8(强调我的)

\n\n
\n

可以在任何实例化之前检查模板的有效性。[注意:知道哪些名称是类型名称可以以这种方式检查每个模板的语法。\xe2\x80\x94尾注]\n 如果出现以下情况,则程序格式错误,无需诊断:

\n\n
    \n
  • (8.1) 无法为模板或模板内 constexpr if 语句的子语句生成有效的特化,并且模板未实例化,或者
  • \n
\n\n

[...]

\n
\n\n

是的,确实,你的False<T>依赖T。问题是泛型 lambda 本身就是一个模板,并且False<T>不依赖于 lambda 的任何模板参数。

\n\n

对于Tfalse False<T>,静态断言将始终为 false,无论将哪个模板参数发送到 lambda。

\n\n

编译器可以看到,对于 template 的任何实例化operator(),静态断言将始终为当前 T 触发。因此会出现编译器错误。

\n\n

解决这个问题的方法是依赖于x

\n\n
template<typename T>\nvoid foo() {\n\n    auto f = [](auto x) {\n        if constexpr(x < 0) {\n            static_assert(False<decltype(x)>, "AAA");\n        }\n    };\n\n    f(std::integral_constant<int, 1>{});\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

实例

\n