【问题标题】:Explicit instantiation of class templates is ignored?类模板的显式实例化被忽略?
【发布时间】:2015-11-08 20:18:23
【问题描述】:

我想检查 extern 关键字是否确实阻止了在翻译单元中生成类代码:

template<class> struct always_false : std::false_type {};

template <typename T> class A{
    static_assert(always_false<T>::value, "If this fires, 'A' is instantiated");
};

extern template class A<int>; //error: static assertion failed: If this fires, 'A' is instantiated|

int main(){
//    A<int> f;
}

如果这是我唯一的源文件,为什么前面的代码仍然会从static_assert 产生错误?据我从extern 的显式使用中了解到,这应该会阻止为类A&lt;int&gt; 生成任何代码,并且链接器会负责查找稍后的显式实例化定义(在实际编写代码的翻译单元中) ) 来匹配A&lt;int&gt; 的任何使用。

但是,正如编译错误所指示的那样,显式实例化声明似乎正在此翻译单元本身中生成代码。如果我注释掉 extern template class A&lt;int&gt; 一切正常。我使用的是 GCC 4.9.2。但似乎clang 3.5.1 也会引发此错误。

另外,这也会引发相同的断言错误:

template<class> struct always_false : std::false_type {};

template <typename T> class A{
public:
    void test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }
};

extern template void A<int>::test();

int main(){
    A<int> a;
    a.test();
}

在这里,我预计成员函数 A&lt;int&gt;::test() 甚至不会被实例化,并再次等待链接,然后再“查找”该函数的代码,但看起来代码是在同一个翻译单元中生成的。但是,如果我取出static_assert:

template <typename T> class A{
public:
    void test() { }
};

extern template void A<int>::test();

int main(){
    A<int> a;
    a.test();
}

然后我得到了我期望的错误,表明A&lt;int&gt;::test() 没有被实例化并且存在链接器错误:

**undefined reference to `A<int>::test()'|**

如果test() 从未实例化,为什么static_assert 会抛出错误?

【问题讨论】:

    标签: c++ c++11 instantiation explicit


    【解决方案1】:

    你的前提是错误的。 extern template 阻止函数模板(包括类模板的成员函数)生成目标代码,但不阻止类体的实例化。

    编辑: 回答更新后的问题:成员函数是在类中内联定义的,因此编译器仍将实例化它,以便在必要时内联它。如果你定义的函数不符合规定,你不会得到错误(通过 Godbolt 尝试了 GCC 5.2.0)。

    #include <type_traits>
    
    template<class> struct always_false : std::false_type {};
    
    template <typename T> class A{
    public:
        void test();
    };
    
    template <typename T>
    void A<T>::test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }
    
    extern template void A<int>::test();
    
    int main(){
        A<int> a;
        a.test();
    }
    

    【讨论】:

    • 如果是这样的话就足够了。我正在阅读 C++ Primer 第 5 版的 16.1.5,它看起来就像 extern 完全阻止了类体的实例化。不过,我已经对我的 OP 进行了一些额外的查询。
    • @AntiElephant 更新答案。
    • 我明白了,N3337 的 [14.7.2][10] 也概述了这个内联异常。所以对a.test() 的调用是内联的,这就是导致static_assert 抛出的原因。但是,从test() 中删除断言后,即使函数在调用时内联,它仍然会产生“未定义引用”的错误?这只是为了保持一致性吗?
    • [11] - 一个实体,它是显式实例化声明的主题,并且其使用方式也会导致翻译单元中的隐式实例化 (14.7.1)应该是程序中某处显式实例化定义的主题;否则程序格式错误。结合 [14.7.2][10] 这表明该函数仍然是隐式实例化的,因为它是内联的,但是因为有一个没有相应定义的显式声明,它仍然会引发链接器错误。老实说,我觉得这种行为很奇怪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 2013-02-09
    • 2021-11-25
    • 1970-01-01
    • 2021-05-02
    • 1970-01-01
    相关资源
    最近更新 更多