LLVM STLExtras 中的错误:预期 unqualified-id 在 'const' 之前与 GCC11

Error in LLVM STLExtras: expected unqualified-id before 'const' with GCC11

我刚刚将编译器升级到 GCC 11.1.0,但在尝试编译使用 LLVM 的程序时遇到了问题。

发生的错误如下:

In file included from /usr/include/llvm/IR/DebugInfo.h:19,
                 from /home/<redacted>/src/sword/format/llvm.hpp:12,
                 from /home/<redacted>/src/main.cpp:9:
/usr/include/llvm/ADT/STLExtras.h:1793:18: error: expected unqualified-id before ‘const’
 1793 |   result_pair<R>(const result_pair<R> &Other)
      |                  ^~~~~
/usr/include/llvm/ADT/STLExtras.h:1793:18: error: expected ‘)’ before ‘const’
 1793 |   result_pair<R>(const result_pair<R> &Other)
      |                 ~^~~~~
      |                  )
/usr/include/llvm/ADT/STLExtras.h:1843:22: error: expected unqualified-id before ‘const’
 1843 |   enumerator_iter<R>(const enumerator_iter<R> &Other) : Result(Other.Result) {}
      |                      ^~~~~
/usr/include/llvm/ADT/STLExtras.h:1843:22: error: expected ‘)’ before ‘const’
 1843 |   enumerator_iter<R>(const enumerator_iter<R> &Other) : Result(Other.Result) {}
      |                     ~^~~~~
      |                      )

我查看了源代码,但不确定 LLVM 的代码是 ill-formed,还是 GCC 的错误。

STLExtra.h中第1793行的错误与class的定义有关:

template <typename R> struct result_pair {
  ...
  result_pair<R>(const result_pair<R> &Other)
      : Index(Other.Index), Iter(Other.Iter) {}
  ...
};

通常,当我定义带有模板参数的class时,我不会将模板参数放在构造函数的声明中(即result_pair(...))。 事实上,当我将构造函数从 result_pair<R>(...) 修改为 result_pair(...) 时,我的程序编译得很好(对 enumerator_iter 也是如此)。

那么,是不是LLVM的代码错了,而GCC是对的呢?或者这是 GCC 回归?我对 C++ 标准的了解太有限,无法有效地 Google 检查这是否是已知错误...非常感谢任何帮助。

此外,我尝试使用 clang-11 编译我的程序,但它根本无法编译,因为 C++20 功能还不存在(具体来说,它在模板中的某处失败)。所以,我无法通过这种方式缩小罪魁祸首。

P.S。由于它在修改后编译得很好,我想我暂时只保留 header 修改。

您提到您使用的是 C++20。从 C++20 开始,这确实是一个错误:对构造函数使用 simple-template-ids 被认为容易出错且多余,因此被删除。参见 [diff.cpp17.class]/2

如您所述,您可以使用注入的 class 名称来修复错误:

template <typename R>
struct result_pair {
  // ...
  result_pair(const result_pair &Other)
      : Index(Other.Index), Iter(Other.Iter) {}
  // ...
};

特别是,这可以防止在构造函数声明中意外使用错误的模板参数,这显然不应该编译。

根据 N. Shead 的回答,这里有一个补丁文件,可用于修复 LLVM 12.0.1 上的错误:

--- /usr/include/llvm/ADT/STLExtras.h.orig  2021-10-21 10:41:36.706064183 -0400
+++ /usr/include/llvm/ADT/STLExtras.h   2021-10-21 10:41:59.442775225 -0400
@@ -1820,9 +1820,9 @@
   result_pair(std::size_t Index, IterOfRange<R> Iter)
       : Index(Index), Iter(Iter) {}
 
-  result_pair<R>(const result_pair<R> &Other)
+  result_pair(const result_pair &Other)
       : Index(Other.Index), Iter(Other.Iter) {}
-  result_pair<R> &operator=(const result_pair<R> &Other) {
+  result_pair &operator=(const result_pair &Other) {
     Index = Other.Index;
     Iter = Other.Iter;
     return *this;
@@ -1856,22 +1856,22 @@
   result_type &operator*() { return Result; }
   const result_type &operator*() const { return Result; }
 
-  enumerator_iter<R> &operator++() {
+  enumerator_iter &operator++() {
     assert(Result.Index != std::numeric_limits<size_t>::max());
     ++Result.Iter;
     ++Result.Index;
     return *this;
   }
 
-  bool operator==(const enumerator_iter<R> &RHS) const {
+  bool operator==(const enumerator_iter &RHS) const {
     // Don't compare indices here, only iterators.  It's possible for an end
     // iterator to have different indices depending on whether it was created
     // by calling std::end() versus incrementing a valid iterator.
     return Result.Iter == RHS.Result.Iter;
   }
 
-  enumerator_iter<R>(const enumerator_iter<R> &Other) : Result(Other.Result) {}
-  enumerator_iter<R> &operator=(const enumerator_iter<R> &Other) {
+  enumerator_iter(const enumerator_iter &Other) : Result(Other.Result) {}
+  enumerator_iter &operator=(const enumerator_iter &Other) {
     Result = Other.Result;
     return *this;
   }

将以上内容保存到 ~/STLExtras.h.patch 然后通过以下方式应用补丁:

$ sudo patch --backup /usr/include/llvm/ADT/STLExtras.h ~/STLExtras.h.patch
patching file /usr/include/llvm/ADT/STLExtras.h

原件将保存到 /usr/include/llvm/ADT/STLExtras.h.orig,您可以保留或删除。