我在使用此代码的 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';
}
编译器资源管理器
来自叮当的莫名其妙的错误消息:
basic_string.h:356:10:注意:常量表达式中不允许将没有活动成员的联合的成员“_M_local_buf”分配给成员
代码没有问题,如果你使用libc(-stdlib=libc
),Clang也可以正确编译它。编译器资源管理器默认使用 libstdc for Clang (-stdlib=libstdc
)。两者之间似乎只是存在兼容性问题。我的猜测是,libstdc 正在以一种技术上不允许在常量表达式中实现 constexpr
字符串的方式,但 GCC 在这方面往往不如 Clang 那么严格,因此 Clang 在实现时失败,而 GCC 没有问题。
更准确地说,在libstdc中,std::string
的实现包含一个匿名联合,其中一个成员是一个名为_M_local_buf
的_CharT
数组。那么,如果计算发生在常量表达式计算中,libstdc 将执行以下操作(从 https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/basic_string.h#L355 开始)
for (_CharT& __c : _M_local_buf)
__c = _CharT();
为了将工会成员_M_local_buf
设置为活动并将其归零。
问题在于,在此循环之前,没有将联合的任何成员设置为活动,并且通过引用而不是直接通过名称或通过成员访问表达式设置成员(子对象)在技术上没有在标准中指定将成员设置为活动。因此,从技术上讲,它具有未定义的行为,这就是为什么Clang(正确)不想接受它作为常量表达式的原因。如果事先将成员设置为活动状态,例如使用额外的行 _M_local_buf[0] = _CharT();
那就没问题了。
当然,直接设置 _M_local_buf[0]
似乎有点愚蠢,但通过引用__c
设置它就不行了,这可能就是为什么 GCC 仍然接受它作为常量表达式的原因,我想可能存在 CWG 问题。
这里有一个与非常相似的问题相关的libstdc错误报告,但这已经应该修复一段时间了。
似乎提交 https://github.com/gcc-mirror/gcc/commit/98a0d72a610a87e8e383d366e50253ddcc9a51dd 已经(重新)引入了您在此处看到的特定问题。在提交之前,成员已正确设置为活动状态。打开一个关于这个问题的问题可能是值得的,尽管如上所述,Clang在这里非常迂腐,并且正如链接的错误报告也说的那样,可能应该更改标准以允许这样做。