【问题标题】:Clang and Gcc disagree on explicit specialization after instantiationClang 和 Gcc 在实例化后的显式特化上存在分歧
【发布时间】:2019-05-28 23:09:30
【问题描述】:

在我正在审查的一些代码中,我遇到了 Clang 和 Gcc 不同意的情况。看了一会儿,我似乎无法弄清楚谁是对的。

免责声明:我知道有更好的单例模式,但这是代码中使用的模式。

注意事项:

  • Ubuntu 上的 gcc 7.4.0(无错误)

  • Ubuntu 上的 clang 6.0.0(引发错误)

  • 所有 C++11 后 ISO 版本似乎都存在差异,但我之前没有尝试过。

foo.hh

#include "sing.hh"

class Foo {
    public: 
    Foo();
    ~Foo();
    static Foo *getSingleton(){
        return singleton<Foo>::instance();  
    }
};

foo.cc

include "foo.hh"
//removing this line results in the error for clang disappearing 
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr; 
int main(){};

sing.hh

template<typename T>
class singleton{
    typedef T *(*GetInstance)(void);
public:

  static GetInstance instance;

};

结果:

$ clang++  foo.cc
foo.cc:3:56: error: explicit specialization of 'instance' after instantiation
template<> singleton<Foo>::GetInstance singleton<Foo>::instance = nullptr;
                                                       ^
./foo.hh:10:32: note: implicit instantiation first required here
        return singleton<Foo>::instance();  
                               ^
1 error generated.



$ g++  foo.cc <- No Errors

【问题讨论】:

    标签: c++ templates gcc clang iso


    【解决方案1】:

    这两个编译器在技术上都没有错误。代码无效,但 C++ 实现不需要提供有关此类错误的诊断消息。

    标准[temp.expl.spec]/6 说(强调我的):

    如果模板、成员模板或类模板的成员是显式特化的,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在每个翻译单元中发生使用; 无需诊断。

    您可以通过在 sing.hh 中定义 singleton 之后立即声明显式特化来解决此问题:

    struct Foo;
    template<> singleton<Foo>::GetInstance singleton<Foo>::instance;
    

    或者,如果您希望所有特化初始化为空指针,您可以只定义通用类模板的成员,同样可能在 sing.hh 中。那么就不需要显式特化了,除非你想为某些特定类型使用不同的初始化器。

    template<typename T>
    typename singleton<T>::GetInstance singleton<T>::instance = nullptr;
    

    【讨论】:

    • “您可以通过在 sing.hh 中定义单例后立即声明显式特化来解决此问题” - 我认为这行不通,因为 Foo 会尚未宣布。您必须转发声明Foo。第二种方法可行,对我来说,无论如何听起来都是最安全的方法。 +1 表示不需要诊断。我错过了。
    【解决方案2】:

    来自这个答案here 和cpp 参考here。

    显式特化可以在其主要的任何范围内声明 模板可以定义[...]。

    显式特化必须出现在非特化之后 模板声明。

    必须在第一次使用之前声明专业化,这会导致 隐式实例化,在每个翻译单元中使用 发生

    如果显式特化在sing.cpp 文件中,那么编译器都不会抱怨。或者,您可以使用前向声明来执行以下操作,clang 和 gcc 都会很高兴。

    #include <iostream>
    
    template<typename T>
    struct singleton
    {
        typedef T *(*GetInstance)(void);
    
        static GetInstance instance;
    };
    
    template<>
    singleton<struct Foo>::GetInstance singleton<struct Foo>::instance = nullptr;
    
    struct Foo
    {
        static Foo *getSingleton()
        {
            return singleton<Foo>::instance();  
        }
    };
    
    int main()
    {
    
    }
    

    在线代码示例:https://rextester.com/SPZLS83155

    【讨论】:

    • 这里没有“前向声明”。
    • @aschepler: struct Foo 在显式模板特化中。
    猜你喜欢
    • 2012-09-25
    • 2011-12-08
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    相关资源
    最近更新 更多