为什么 'extern template class' 技术没有按预期工作?

Why does 'extern template class' technique not work as expected?

原题已细化

给定一个名为main.cpp的源代码文件如下:

#include <string>
extern template class std::basic_string<char>;

template<typename T>
struct A
{
    T n = {};

    T get() const
    {
        return n;
    }
};

extern template struct A<int>;

int main()
{
    auto a = A<int>{}.get(); // undefined reference to `A<int>::get() const'
    auto b = static_cast<int>(std::string{}.size()); // ok

    return a + b;
}

我的预期:

请注意,源代码有 extern template class std::basic_string<char> 但没有 template class std::basic_string<char>

因此,编译器不会实例化 class std::basic_string<char>,然后 g++ main.cpp 会在第 std::string{}.size() 行和第 [=18 行一样导致 link 错误=].

我观察到的:

可以g++ main.cpp在线std::string{}.size()Online demo

为什么 extern template class 技术没有按预期工作?

它不起作用的原因至少有两个。

一个 不禁止库class 和函数模板实例被实现声明为extern templategcclibstdc++ 就是这样做的。

$ g++ -E main.cpp | grep 'extern.*string'
extern template class basic_string<char>;      // <-- from the library
extern template class basic_string<wchar_t>;   // <-- from the library
extern template class std::basic_string<char>; // <-- your line

标准库实现包含显式实例化定义(您可以挖掘源代码)。

两个 作为显式实例化声明的主题并且还以其他方式使用会导致翻译单元中隐式实例化的实体应为主题程序某处的显式实例化定义;否则程序格式错误,不需要诊断 [temp.explicit].

NDR 的一个可能理由是 extern template 不应阻止内联和其他不涉及链接的用途。事实上,with -O2 A::get 是内联的,程序构建得很好。没有任何成熟的技术可以强制拒绝该程序。它可以因为链接器错误而被拒绝,但不要求出现链接器错误。