C++ 嵌套模板显式专门化

C++ 嵌套模板显式专门化,c++,C++,我有一些编译器行为-在VisualC++和g++上不同-我不理解(对于这两种编译器)。我将用英语描述它,但也许只看下面的代码会更容易 它与具有成员类模板和成员函数模板的类模板有关。我正试图同时 显式专门化外部类模板(保留成员模板未专门化) 在外部类模板的显式专门化中显式专门化成员模板 我发现,对于这两个编译器,如果我执行#1(显式专门化外部类模板)或#2(显式专门化成员模板),那么一切都可以很好地编译(并且实例化与预期一样) 但是,如果我试图同时做1和2(我相信是按照正确的顺序声明的),我会发现

我有一些编译器行为-在VisualC++和g++上不同-我不理解(对于这两种编译器)。我将用英语描述它,但也许只看下面的代码会更容易

它与具有成员类模板和成员函数模板的类模板有关。我正试图同时

  • 显式专门化外部类模板(保留成员模板未专门化)
  • 在外部类模板的显式专门化中显式专门化成员模板
  • 我发现,对于这两个编译器,如果我执行#1(显式专门化外部类模板)或#2(显式专门化成员模板),那么一切都可以很好地编译(并且实例化与预期一样)

    但是,如果我试图同时做1和2(我相信是按照正确的顺序声明的),我会发现

    • 使用g++,我可以对成员类模板执行此操作,但会对成员模板类声明产生编译器错误
    • 对于VC++,我得到两个成员模板的编译器错误
    下面是外部类模板的主要定义。在以下所有情况下都是相同的:

    // Class template with a member class template and a member function template
    template <int I>
    struct OuterClass
    {
        template <int J> struct InnerClass {};
        template <int J> static void InnerFunc() {}
    };
    
    //带有成员类模板和成员函数模板的类模板
    模板
    结构外部类
    {
    模板结构内部类{};
    模板静态无效InnerFunc(){}
    };
    
    这里只做#1(显式专门化外部类模板)。这可以很好地编译,并按照预期进行实例化

    // Explicit specialization of outer class template,
    // leaving member templates unspecialized.
    template <>
    struct OuterClass<1>
    {
        template <int J> struct InnerClass {};
        template <int J> static void InnerFunc() {}
    };
    
    // Explicit specialization of inner templates for
    // an explicit specialization of outer class template
    template <> template <> struct OuterClass<1>::InnerClass<1> {};
    template <> template <> void OuterClass<1>::InnerFunc<1>() {}
    
    //外部类模板的显式专门化,
    //将成员模板保留为非专用模板。
    模板
    结构外部类
    {
    模板结构内部类{};
    模板静态无效InnerFunc(){}
    };
    
    这里只做#2(显式专门化成员模板)。这可以很好地编译,并按照预期进行实例化

    // Explicit specialization of outer class template,
    // leaving member templates unspecialized.
    template <>
    struct OuterClass<1>
    {
        template <int J> struct InnerClass {};
        template <int J> static void InnerFunc() {}
    };
    
    // Explicit specialization of inner templates for
    // an explicit specialization of outer class template
    template <> template <> struct OuterClass<1>::InnerClass<1> {};
    template <> template <> void OuterClass<1>::InnerFunc<1>() {}
    
    //内部模板的显式专门化
    //外部类模板的显式专门化
    模板模板结构OuterClass::InnerClass{};
    模板模板void OuterClass::InnerFunc(){}
    
    这里尝试同时执行#1和#2-将前面的两个代码段粘贴在一起:

    // Explicit specialization of outer class template,
    // leaving member templates unspecialized.
    template <>
    struct OuterClass<1>
    {
        template <int J> struct InnerClass {};
        template <int J> static void InnerFunc() {}
    };
    
    // Explicit specialization of inner templates for
    // an explicit specialization of outer class template
    template <> template <> struct OuterClass<1>::InnerClass<1> {};  // Line A
    template <> template <> void OuterClass<1>::InnerFunc<1>() {}    // Line B
    
    //外部类模板的显式专门化,
    //将成员模板保留为非专用模板。
    模板
    结构外部类
    {
    模板结构内部类{};
    模板静态无效InnerFunc(){}
    };
    //内部模板的显式专门化
    //外部类模板的显式专门化
    模板模板结构OuterClass::InnerClass{};//A线
    模板模板void OuterClass::InnerFunc(){}//行B
    
    g++很好地编译了“A行”(实例化与预期一样)。但是g++在第B行给出了一个编译器错误:“模板参数列表太多”

    VC++给出了“A行”和“B行”的编译器错误(太乱,太多,无法在此详述)

    同样,对于这两个编译器,“行A”和“行B”在外部类模板的显式专门化之后没有出现时都可以编译

    在我看来,一切都应该很好。那么谁是对的——我、g++还是VC++?更重要的是,为什么


    请理解这不是一个“我如何完成X”的问题,这是一个“我想完全理解C++”的问题。如果你花点时间通读并思考一下,我会感谢你的……我希望我能尽可能地把它写下来。

    我在这个问题上花了很多时间,我想我已经弄明白了——我所说的“弄明白”是指我确切地知道在我尝试过的两种编译器(MSVC和g++)上编译什么和不编译什么,以及每个编译器使用的语法。这一切都很难看,但它是可预测和可重复的-我有很多示例代码没有显示在这里,我从中推断出结果。为了清楚并避免我的失望,这里的描述不是我的理论,而是在数百个示例案例中观察到的编译器行为

    在高级别:

    • MSVC中存在一个bug(?),在某些定义良好的情况下,它会阻止编译嵌套在类模板中的函数模板的完全显式专门化
    • 两个编译器都可以编译嵌套在另一个类模板中的类模板的显式专门化。但是,对于MSVC和g++,“模板”必须出现多少次的规则是不同的。因此,对于可移植代码,在某些情况下必须使用条件编译
    我在这里给出的详细描述可能过于简短,普通读者无法理解,但我还是会尝试一下

    • 通常,在声明/定义类模板(可能在嵌套类模板的深层链中)成员的专门化时,“模板”必须出现的次数等于“覆盖”所声明的专门化的最近类模板专门化的“专门化跳跃”次数,到正在声明的专门化。
      • 称之为“最后一跳规则”
      • 这适用于两个编译器上的所有类型的可专门化成员(类/类模板、函数/函数模板、变量/变量模板和枚举),只有一个例外(见下文)
    • 用于声明/定义嵌套在另一个类模板(可能在嵌套类模板的深层链中)中的类的专门化
      • 对于MSVC:“模板”必须出现的次数是“最后一跳规则”
      • 对于g++:这是一个“Las Hop规则”不适用的情况。(无论出于什么原因,我猜g++中有一个bug?)。在这种情况下,“模板”必须出现的次数等于所声明的专门化的“深度”,减去覆盖所声明的专门化的模板类专门化的总数
    • 用于声明/定义特殊