lambda、gcc和msvc中的clang constexpr编译错误.clang bug



我用这段代码在clang中遇到了一个编译错误。我认为它没有任何问题,它在msvc和gcc中都可以工作。我是错过了什么,还是一只叮当作响的虫子?

#include <iostream>
#include <string>
#include <type_traits>
constexpr bool do_tests()
{
// prints error message at runtime if test is false
auto verify = [](bool test, std::string message = "error") {
if (!std::is_constant_evaluated() && !test)
std::cout << message << 'n';
return test;
};
return verify(1 == 1, "test");
};

int main()
{
constexpr bool b = do_tests();
std::cout << b << 'n';
}

编译器资源管理器

来自clang的无法解释的错误消息:

basic_string.h:356:10:注意:常量表达式中不允许向没有活动成员的联合的成员'_M_local_buf'赋值

如果使用libc++(-stdlib=libc++(,代码没有问题,Clang也会正确编译它。默认情况下,编译器资源管理器会将libstdc++用于Clang(-stdlib=libstdc++(。两者之间似乎存在兼容性问题。我的猜测是,libstdc++实现constexpr字符串的方式在技术上是常量表达式中不允许的,但GCC在这方面往往不如Clang严格,因此Clang在实现上失败了,而GCC对此没有问题。


更准确地说,在libstdc++中,std::string的实现包含一个匿名并集,其中一个成员是名为_M_local_buf_CharT数组。如果求值发生在常量表达式求值中(从https://github.com/gcc-mirror/gcc/blob/releases/gcc-12.2.0/libstdc++-v3/include/bits/basic_string.h#L356(

for (_CharT& __c : _M_local_buf)
__c = _CharT();

以便将联合成员CCD_ 7设置为活动并将其归零

问题是,在此循环之前,没有将并集的成员设置为活动成员,并且通过引用而不是直接通过名称或成员访问表达式来设置成员(子对象(,从技术上讲,标准中没有指定将成员设置为活动成员。因此,从技术上讲,它有未定义的行为,这就是为什么Clang(正确地(不想接受它作为常量表达式。如果事先将成员设置为活动,例如使用额外的行_M_local_buf[0] = _CharT();,则可以。

当然,直接设置_M_local_buf[0]似乎有点傻,但通过引用__c设置它则不然,但这可能有充分的理由。引用通常不能保证引用在编译时已知的特定对象,因此编译器可能更难识别表达式是否需要限制其周围的某些优化,如果活动成员发生更改,这些优化将无效。


这里有一个与非常类似的问题有关的libstdc++错误报告,但这应该已经修复了一段时间。

似乎承诺https://github.com/gcc-mirror/gcc/commit/98a0d72a610a87e8e383d366e50253ddcc9a51dd已经(重新(介绍了您在这里看到的特定问题。在提交之前,该成员已正确设置为活动状态。尽管如上所述,Clang在这里非常迂腐,而且正如相关的错误报告所说,标准可能应该改变以允许这样做,但公开一个问题可能是值得的。


更新:

GCC 13.1和12.3版中有一个对libstdc++的提交,它确实修复了使用Clang编译时的问题。请参阅提交https://github.com/gcc-mirror/gcc/commit/52672be7d328df50f9a05ce3ab44ebcae50fee1b:

_M_local_buf[__i] = _CharT();

通过将隐式(*this).添加到_M_local_buf,这现在满足class.union.general/5中所需的形式,以开始_M_local_buf阵列成员的生存期,并因此将其设置为活动。

最新更新