簡體   English   中英

嵌套模板顯式特化

[英]Nested template explicit specialization

我有一些編譯器行為——在 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++”的問題。 如果你花時間通讀並思考它,你有我的感謝......我希望我盡可能地把它歸結起來。

我在這個問題上花費了大量時間,我想我已經弄清楚了 - “弄清楚”的意思是我確切地知道在我嘗試過的兩個編譯器(MSVC 和 g++)上什么會編譯什么不會編譯,以及每個編譯器使用什么語法。 這一切都很丑陋,但它是可預測和可重復的——我有很多示例代碼沒有在這里顯示,我從中推斷出結果。 為了清楚起見並避免我感到沮喪,這里的描述不是我的理論,它是編譯器在數百個示例案例中觀察到的行為。

在高層次上:

  • MSVC 中存在一個錯誤 (?),在某些明確定義的情況下,它會阻止編譯嵌套在類模板中的函數模板的完整顯式特化。
  • 兩個編譯器總是可以編譯嵌套在另一個類模板中的類模板的顯式特化。 但是,對於 MSVC 和 g++,“template<>”必須出現多少次的規則是不同的。 因此,對於可移植代碼,在某些情況下您必須使用條件編譯。

我在這里給出的詳細描述可能過於簡短,普通讀者無法理解,但無論如何我都會嘗試。

  • 通常,在聲明/定義類模板成員的特化時(可能在嵌套類模板的深層鏈中),“模板<>”必須出現的次數等於從“覆蓋”所聲明的特化的最接近的類模板特化,到所聲明的特化。
    • 稱其為“最后一跳規則”
    • 這適用於兩種編譯器上的所有類型的特殊化成員(類/類模板、函數/函數模板、變量/變量模板和枚舉),只有一個例外(見下文)
  • 用於聲明/限定嵌套在另一個類模板模板的特化(可能的嵌套類模板的深鏈)
    • 對於 MSVC:“模板<>”必須出現的次數是“最后一跳規則”
    • 對於 g++:這是“Las Hop 規則”不適用的一種情況。 (無論出於何種原因,我猜是 g++ 中的一個錯誤?)。 在這種情況下,“template<>”必須出現的次數等於所聲明的特化的“深度”減去覆蓋所聲明的特化的模板類特化的總數。
  • 用於聲明/定義嵌套在類模板中的函數模板的特化(可能在嵌套類模板的深層鏈中)
    • 對於 MSVC:在某些情況下這不會編譯。 我推斷出條件會編譯,什么時候不會編譯,但這里描述的太復雜了。 我猜 MSVC 中存在一個錯誤 - g++ 始終有效。 當它起作用時,“模板<>”出現在“最后一跳規則”中的次數(對於兩個編譯器)。
    • 對於 g++:這總是有效的。 “模板<>”出現在“最后一跳規則”中的次數。

下面是一些示例代碼,顯示了在定義具有特定專業化鏈的嵌套類模板時,“template<>”必須出現的次數。 這在 MSVC 和 g++ 上編譯(使用條件編譯)。 此代碼不涉及嵌套函數模板。

#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
{ 
    //...
}

這里的問題(就像你的另一個問題一樣)是,一旦為(整個)類模板聲明了顯式特化,該特化在語法上就是一個普通類(盡管名稱包含標點符號):

template<int> struct A {
  void f();
};
template<int I> void A<I>::f() {}

// A specialization of only a member does use template<>:
template<> void A<0>::f() {/*...*/}

template<> struct A<1> {
  void g();
};
// A definition of a member of a specialization doesn't use template<>:
void A<1>::g() {}

將相同的邏輯應用於成員模板會產生

template<int> struct A {
  template<class> void f();
  template<> void f<void>() {}
};
template<int I> template<class T> void A<I>::f() {}

template<> template<class T> void A<0>::f() {/*...*/}
template<> template<> void A<-1>::f<short>() {/*...*/}
// template<int I> template<> void A<I>::f<int>() {/*...*/}

template<> struct A<1> {
  template<class> void g();
};
template<class T> void A<1>::g() {}
template<> void A<1>::g<char>() {/*...*/}

注釋掉的可能性不起作用:您不允許在該類模板之外專門化(非專門化)類模板的成員模板,盡管您可以在模板內部這樣做(盡管 GCC 和 ICC 不實現這一點)或為一個完整的類模板特化的成員模板。

如果在完全專業化的情況下,成員碰巧具有相同的名稱,則像這樣的示例當然有點違反直覺。 在存在編譯器錯誤的情況下,它們也會更加混亂!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM