簡體   English   中英

gcc和clang在運算符重載解析期間隱式實例化模板參數

[英]gcc and clang implicitly instantiate template arguments during operator overload resolution

考慮以下代碼:

struct A; // incomplete type

template<class T>
struct D { T d; };

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;  // doesn't compile; complain that D<A>::d has incomplete type
    u.operator=(v); // compiles
}

演示 由於u.operator=(v)編譯,但u = v; 不會,在后一個表達式的重載解析期間,編譯器必須隱式實例化D<A> - 但我不明白為什么需要實例化。

為了使事情更有趣,這段代碼編譯:

struct A; // incomplete type

template<class T>
struct D; // undefined

template <class T>
struct B { int * p = nullptr; };

int main() {
    B<D<A>> u, v;
    u = v;
    u.operator=(v);
}

演示

這里發生了什么? 為什么u = v; 導致D<A>的隱式實例化 - 一種在B的定義中沒有使用的類型 - 在第一種情況下但不是第二種情況?

問題的全部內容是ADL踢:

N3797 - [basic.lookup.argdep]

當函數調用(5.2.2)中的postfix-expression是非限定id時,可以搜索在通常的非限定查找(3.4.1)期間未考慮的其他命名空間,並且在這些命名空間中,命名空間范圍的朋友函數或函數可以找到不可見的模板聲明(11.3)。

以下:

對於函數調用中的每個參數類型T,存在一組零個或多個關聯的命名空間以及要考慮的一組零個或多個關聯的類。 [...]命名空間和類的集合按以下方式確定:

  • 如果T是類類型[..],則其關聯的類是:...如果T是類模板特化,則其關聯的名稱空間和類還包括:與模板類型提供的模板參數類型相關聯的名稱空間和類參數

D<A>是一個關聯的類,因此在列表中等待輪到它。

現在為有趣的部分[temp.inst] / 1

除非已經顯式實例化了類模板特化(14.7.2)或顯式專用(14.7.3),否則當類類型的完整性影響程序的語義時 ,類模板特化將被隱式實例化[...]

可以認為D<A>類型的完整性並不影響該程序的所有語義,但[basic.lookup.argdep] / 4表示:

在考慮關聯的命名空間時,查找與關聯命名空間用作限定符時執行的查找相同(3.4.3.2),但以下情況除外:

[...]在關聯類中聲明的任何命名空間范圍的朋友函數或朋友函數模板在其各自的命名空間中都是可見的,即使它們在普通查找期間不可見(11.3)

即類類型的完整性實際上影響了朋友聲明 - >類類型的完整性因此影響了程序的語義。 這也是你的第二個樣本有效的原因。

TL; DR D<A>被實例化。

最后一個有趣的觀點是為什么ADL首先開始

u = v; // Triggers ADL
u.operator=(v); // Doesn't trigger ADL

§13.3.1.2/ 2規定不能有非成員operator= (除了內置operator= )。 加入[over.match.oper] / 2:

根據通常的非限定函數調用(3.4.2)中的名稱查找規則,非成員候選集合是表達式上下文中operator @的非限定查找的結果,除了忽略所有成員函數。

邏輯結論是:如果表11中沒有非成員形式,則執行ADL查找沒有意義。但[temp.inst] p7放寬了這個:

如果重載解析過程可以在不實例化類模板定義的情況下確定要調用的正確函數,則未指定該實例化是否實際發生。

這就是為什么clang首先觸發整個ADL -> implicit instantiation過程的原因。

r218330開始(在編寫本文時,它已在幾分鍾前提交),此行為已更改為根本不執行ADL for operator=


參考

感謝理查德史密斯和大衛布萊基幫助我解決這個問題。

好吧,我認為在Visual Studio 2013代碼應該看起來像(沒有= nullptr):

  struct A; // incomplete type

  template<class T>
  struct D { T d; };

  template <class T>
  struct B { int * p; };

  int void_main() {
    B<D<A>> u, v;
    u = v;          //  compiles
    u.operator=(v); // compiles
    return 0;
    }

在這種情況下,它應該編譯得很好,因為不完整的類型可以用於特定的模板類特化用法。

至於運行時錯誤 - 沒有初始化使用的變量v - 它是正確的 - struct B沒有任何構造函數=> B :: p沒有初始化並且可能包含垃圾。

暫無
暫無

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

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