【问题标题】:Compiler segfault during explicit template instantiation on clang 11在 clang 11 上显式模板实例化期间的编译器段错误
【发布时间】:2021-01-07 13:27:17
【问题描述】:

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

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 错误跟踪器上提交了报告。 尽管如此,我仍然对代码的实际正确性感到好奇。

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

我不认为上面是格式错误的,我错过了什么吗?

【问题讨论】:

  • 无论输入如何,编译器都不应崩溃(即使这是正式允许的“未定义行为”),因此可能需要提交错误报告。
  • 编译器崩溃是编译器错误,并没有说明代码的正确性。要解决编译器崩溃问题,您几乎必须进行猜测和测试。

标签: c++ templates language-lawyer clang++


【解决方案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>;

使用例如如以下示例(完整的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
}

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


详情

[temp.explicit]/14 需要明确的实例化声明 [emphasis mine]:

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

这些要求都满足了上面的例子,就像A 的定义在[temp.explicit]/5 的专门化的明确定义之前的要求一样。

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

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

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

(5.1) 被满足,因为A&lt;float&gt; 的显式实例化定义位于单个翻译单元中(一个常见的格式错误的 NDR 错误是不小心将显式实例化定义放在标题中,其中标题包含在多个单个源文件)。 (5.2) 不适用,因为不存在 A 的显式特化(对于任何特化),并且 (5.3) 随后不适用,因为没有可能与显式实例化冲突的 A&lt;float&gt; 特化的显式特化后者的定义。


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

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

【讨论】:

  • 谢谢!我尝试了注入的类名,但没有任何区别。
猜你喜欢
  • 2017-06-19
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 2021-01-18
  • 2016-04-15
  • 2021-04-24
  • 1970-01-01
相关资源
最近更新 更多