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

pla*_*cel 6 c++ templates language-lawyer clang++

以下显式模板实例化导致编译器前端段错误在 LLVM clang++ 11.0on 下x86_64-pc-windows-msvc,使用clang-cl接口 with -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>;
Run Code Online (Sandbox Code Playgroud)

A.cpp

#include "A.h"

template class A<float>;
Run Code Online (Sandbox Code Playgroud)

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

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

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

我不认为上述内容格式错误,我错过了什么吗?

dfr*_*fri 1

你的示例类A

// 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>;
Run Code Online (Sandbox Code Playgroud)

例如在以下示例中使用(此处有完整演示

#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
}
Run Code Online (Sandbox Code Playgroud)

格式良好,编译器崩溃自然是(ICE;内部编译器错误)编译器 bug。除非您自己的程序还包含不小心放置的显式专业化(见下文)或者与上面的最小示例有很大不同,否则您自己的程序同样应该是格式良好的。


细节

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

如果一个实体是同一翻译单元中显式实例化声明和显式实例化定义的主题,则定义应遵循声明。作为显式实例化声明的主题的实体,并且其使用方式也会导致翻译单元中的隐式实例化,该实体应成为程序中某处显式实例化定义的主题;否则程序格式错误,无需诊断。[...] 显式实例化声明不应命名具有内部链接的模板的特化。

A这些要求都满足了上面的示例,就像按照[temp.explicit]/5对 的定义先于其特化的显式定义的要求一样一样。

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

对于给定的模板和给定的一组模板参数,

  • (5.1) 显式实例化定义在程序中最多出现一次,
  • (5.2) 显式专业化最多应在程序中定义一次,如 [basic.def.odr] 中指定的,并且
  • (5.3)显式实例化和显式特化的声明都不应出现在程序中,除非显式实例化遵循显式特化的声明。不需要实施来诊断是否违反此规则。

(5.1) 得到满足,因为 的显式实例化定义A<float>位于单个翻译单元内(常见的格式错误的 NDR 错误是不小心将显式实例化定义放置在标头中,其中标头包含在多个源文件中)。(5.2) 不适用,因为没有显式的A现在专业化(对于任何专业化),并且(5.3)随后不适用,因为不存在可能A<float>与后者的显式实例化定义相冲突的专业化的显式专业化。


最后请注意,根据[temp.local]/2 ,您可以在初始化类模板的静态数据成员时使用注入的类名称AaddressA

template <typename T>
class A {
public:
    T value;
    static constexpr auto address = &A::value;
};
Run Code Online (Sandbox Code Playgroud)