嵌套模板显式专业化

时间:2018-10-19 19:52:04

标签: c++

我有一些我不理解的编译器行为-在VisualC ++和g ++上有所不同(对于任何一个编译器)。我将用英语对其进行描述,但仅看下面的代码也许会更容易。

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

  1. 专门对外部类模板进行专业化处理(不对成员模板进行专门化处理)
  2. 显式地专门化成员模板 中,外部类模板的显式专门化。

我发现,如果我同时执行 #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() {}
};

这里仅执行#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() {}
};

这里仅执行#2(显式专门化成员模板)。这样可以很好地进行编译,并且实例化符合预期。

// 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>() {}

这里尝试同时执行#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

g ++可以很好地编译“ A行”(实例化与预期的一样)。但是g ++给出了B行的编译器错误:“模板模板列表过多”。

VC ++给出了“ A行”和“ B行”的编译器错误(太杂乱,在此不胜枚举)。

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

据我了解,一切都应该编译良好。那么谁是正确的-我,g ++还是VC ++?更重要的是,为什么呢?

请理解这不是“如何完成X”问题,这是“我想完全了解C ++”问题。如果您花时间仔细阅读并仔细考虑,您将非常感谢...我希望我尽可能将其精简。

1 个答案:

答案 0 :(得分:2)

我在这个问题上花费了很多荒谬的时间,我想我已经弄明白了-通过“搞清楚”,我的意思是我确切知道在我尝试过的两个编译器(MSVC和g ++),以及每个编译器使用什么语法。这一切都很难看,但是可以预测并且可以重复-我这里没有显示很多示例代码,我可以从中推论出结果。为了清楚起见,避免我感到沮丧,这里的描述不是我的理论,而是在各种示例案例中观察到编译器的行为。

高级别:

  • MSVC中有一个错误(?),它阻止在某些定义明确的情况下 完全嵌套在类中的 function 模板的完整显式专业化模板。
  • 两个编译器始终可以编译嵌套在另一个类模板中的类模板的显式专业化。但是,对于MSVC与g ++,“ template <>”必须出现多少次的规则是不同的。因此,对于可移植代码,在某些情况下必须使用条件编译。

我在这里给出的详细描述可能太简短了,以至于普通读者无法理解,但是我还是会尝试的。

  • 通常,在声明/定义类模板的成员的特殊化时(可能在嵌套的类模板的深层链中),“ template <>”必须出现的次数等于“专业化”的次数从“覆盖”要声明的专业的最接近的类模板专业到“声明”。
    • 将此称为“上一跳规则”
    • 这适用于两个编译器上的所有各种专用成员(类/类模板,函数/函数模板,变量/变量模板和枚举),但有一个例外(请参见下文)
  • 用于声明/定义嵌套在另一个类模板(可能在深层嵌套的类模板的链)中的 class 模板的特化
    • 对于MSVC:“ template <>”必须出现的次数是“最后一跳规则”
    • 对于g ++:这是“ Las Hop规则”不适用的一种情况。 (出于某种原因,我猜想是g ++中的错误?)。在这种情况下,“ template <>”必须出现的次数等于要声明的专业化的“深度”,减去覆盖要声明的专业化的模板类专业化的总数。
  • 用于声明/定义嵌套在类模板(可能在深层嵌套的类模板中)中的 function 模板的特化
    • 对于MSVC:在某些情况下无法编译。我推断出条件可以编译,什么时候不可以编译,但是这里很难描述。我猜MSVC中有一个错误-g ++始终可以工作。当它起作用时,“ template <>”出现在“最后一跳规则”中(对于两个编译器)。
    • 对于g ++:始终有效。 “模板<>”出现在“上一跳规则”中的次数。

这是一些示例代码,显示了在定义带有特定专业化链的嵌套类模板时,“ template <>”必须出现多少次。这可以在MSVC和g ++上进行编译(使用条件编译)。该代码不涉及嵌套的 function 模板。

#include <boost\predef.h>  // For conditional compilation

// Nested class templates, 3 deep.
template <int I1> struct T1
{
    template <int I2> struct T2
    {
        template <int I3> struct T3 {};
    };
};

// Specialization of the third level of class template.
// "template<>" appears three times here for both MSVC and g++ -
// in this case the rules for both compilers both yield 3.
// Note this class template specialization nests another 2 levels of class templates.
template <> template <> template<> struct T1<1>::T2<1>::T3<1>
{
    template <int I4> struct T4 
    {
        template <int I5> struct T5 {};
    };
};

// Specialize the class template contained in the class template specialization above.
// In this case, the number of times "template<>" must appear differs between MSVC and g++,
// so conditional compilation is used.
#if BOOST_COMP_GNUC
// According to the rule described for g++, "template<>" must appear 4 times: 
// (Overall specialization level of 5) - (1 covering specialization which is T1<1>::T2<1>::T3<1>) = 4
template <> template<> template<> template<> struct T1<1>::T2<1>::T3<1>::T4<1>::T5<1> 
#elif BOOST_COMP_MSVC
// MSVC uses the last hop specialization rule, so "template<>" must appear 2 times -
// because the closest covering specialization, T1<1>::T2<1>::T3<1>, is two hops back.
template <> template<> struct T1<1>::T2<1>::T3<1>::T4<1>::T5<1>    
#else
#error Unsupported compiler!
#endif
{ 
    //...
}