clang 11 显式模板实例化期间的编译器段错误

Compiler segfault during explicit template instantiation on clang 11

以下显式模板实例化导致在 x86_64-pc-windows-msvc 上的 LLVM clang++ 11.0 下编译器前端段错误,使用 clang-cl 接口和 -std=c++17,无论优化级别如何。

A.h

template <typename T>
class A
{
public:

    T value;

    static constexpr auto address = &A<T>::value;
};

extern template class A<float>;

A.cpp

#include "A.h"

template class A<float>;

请注意,由于 C++17 A::address 是一个 内联变量 ,因此 ODR 使用在这里不会成为问题。

编译器的行为显然是错误的,我已经在 LLVM 错误跟踪器上提交了一份报告。 尽管如此,我仍然对代码的实际正确性感到好奇。

是编译器处理不当的未定义行为,还是代码本身没有任何问题,只是编译器的问题。就我个人而言,我在显式模板实例化规范的标准中没有发现任何暗示上述代码错误的内容。

我不认为上面的格式不正确,我是不是漏掉了什么?

你的例子classA

// A.h
#pragma once

template <typename T>
class A {
public:
    T value;
    static constexpr auto address = &A<T>::value;
};

extern template class A<float>;

// A.cpp
#include "A.h"

template class A<float>;

例如使用如以下示例 (full demo here)

#include <iostream>
#include "A.h"

int main() {
    A<float> af{42.F};  // uses explicit instantiation: no implicit instantiation occurs.
    A<int> ai{43};      // implicit instantiation.
    std::cout << af.*A<float>::address   // 42
        << "\n" << ai.*A<int>::address;  // 43
}

是well-formed,自然是编译器崩溃的(ICE;internal compiler error)编译器bug。除非您自己的程序还包含不小心放置的显式特化(见下文)或与上面的最小示例有很大不同,否则您自己的程序同样应该是 well-formed.


详情

[temp.explicit]/14 需要显式实例化声明 [强调 我的]:

If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration. An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required. [...] An explicit instantiation declaration shall not name a specialization of a template with internal linkage.

这些要求都满足了上面的例子,A 的定义要求在根据 [temp.explicit]/5.

明确定义它的特化之前

最后,[temp.spec]/5包含[强调我的额外要求:

For a given template and a given set of template-arguments,

  • (5.1) an explicit instantiation definition shall appear at most once in a program,
  • (5.2) an explicit specialization shall be defined at most once in a program, as specified in [basic.def.odr], and
  • (5.3) both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization. An implementation is not required to diagnose a violation of this rule.

(5.1) 得到满足,因为 A<float> 的显式实例化定义位于单个翻译单元内(一个常见的 ill-formed NDR 错误是不小心将显式实例化定义放在 headers,其中 header 包含在多个源文件中)。 (5.2) 不适用,因为不存在 A 的显式特化(对于任何特化),并且 (5.3) 随后不适用,因为 A<float> 特化不存在可能冲突的显式特化使用后者的显式实例化定义。


最后请注意,根据[temp.local]/2,您可以在初始化静态数据成员时使用injected-class-name A address class 模板 A:

template <typename T>
class A {
public:
    T value;
    static constexpr auto address = &A::value;
};