在lambda内部捕获的constexpr变量失去了其constexpr特性

22

这段代码在g++ (coliru)下编译正常,但在MSVC (godbolt和我的VS2017)下无法编译。

#include <type_traits>
#include <iostream>
template<class T> void f(){
    constexpr bool b=std::is_same_v<T,int>; //#1
    auto func_x=[&](){
        if constexpr(b){ //#error
        }else{
        }
    };
    func_x();
}
int main(){
    f<int>();
}

(6): error C2131: 表达式未评估为常量
(6): 注意:失败原因是在其生命周期外读取变量
(6): 注意:见'this'的使用

哪个编译器(g++ 或 MSVC)错了?
在“见'this'的使用”中,“this”是什么?

如何解决此问题,同时保持编译时保证?

在我的实际情况中,b (#1) 是一个依赖于其他几个constexpr变量的复杂语句。


Coliru使用GCC 8.2;而从https://gcc.godbolt.org/下载的GCC 8.3也无法编译该代码。但Clang 7.0.0可以编译它。 - HolyBlackCat
2个回答

18

Gcc是正确的。 b(作为constexpr变量)实际上不需要被捕获

如果变量是constexpr且没有可变成员,则lambda表达式可以读取变量的值而不必捕捉它

GCC LIVE

似乎如果让b成为static,那么MSVC就可以在没有捕获的情况下访问b

template<class T> void f(){
    constexpr static bool b=std::is_same_v<T,int>;
    auto func_x=[](){
        if constexpr(b){
        }else{
        }
    };
    func_x();
}

MSVC LIVE

而且

如何在保持编译时保证的同时解决它?

我们无法保持捕获变量的constexpr性质。它们成为lambda闭包类型的非静态数据成员,非静态数据成员不能是constexpr


C++标准中是否有提到这个位置? - Nicol Bolas
实际上,C++17似乎直接相反b被lambda隐式捕获;没有任何关于它是常量表达式的警告。 - Nicol Bolas
3
@NicolBolas,由于bconstexpr,直接对它执行lvalue-to-rvalue转换并不构成odr-use。有关标准参考,请参阅[basic.def.odr]/3。 - Brian Bi

10
如何解决这个问题并保持编译时的保证?将constexpr bool标记为static即可解决。
详见Demo
或者,您可以在if constexpr中使用条件语句,而不是将其分配给bool。如下所示:
if constexpr(std::is_same_v<T,int>)

请看演示 请注意,已经有关于MSVC的constexpr和lambda表达式的bug报告。
其中之一是:在lambda中捕获constexpr的问题
另一个是:在lambda中使用if constexpr

这些漏洞被标记为“已修复”,但事实上,这些漏洞在版本17.3.5(Visual Studio Professional 2022)中仍然存在... - Jakub Klinkovský

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接