[英]Necessity of forward-declaring template functions
我最近創建了這個示例代碼來說明C ++ 11可變參數模板函數的用法。
template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo () {}
template <typename... Tail>
void foo (int x, Tail... tail)
{
std :: cout << "int:" << x;
foo (tail...);
}
template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
std :: cout << " ?:" << x;
foo (tail...);
}
foo (int (123), float (123)); // Prints "int:123 ?:123.0"
如果省略了前向聲明foo
的前兩行,則打印int:123int:123
。 這讓一位經驗豐富,知識淵博的C ++程序員感到驚訝。
他確信前向聲明不應該是必要的,因為在兩階段查找的第二階段之前,身體不會被實例化。 他認為編譯器(gcc 4.6)有一個bug。
我相信編譯器是正確的,因為兩個foo
是不同的基本模板函數,並且在第一階段需要鎖定基本模板的選擇,否則你可能通過在它的所有版本之前實例化foo
來違反單定義規則已定義,然后再次定義(考慮鏈接器如何假定冗余模板函數定義相同,可互換和可丟棄)。
那么,誰是對的?
上面鏈接的GOTW很好地解釋了函數模板如何以及為什么不部分專門化,但可變參數模板函數的存在似乎增加了混淆 - foo<int,Tail...>
的直覺應該是部分特化foo<Head,Tail...>
比非變量函數的直覺強,至少對我而言。
GCC(和Clang)是對的。 MSVC會錯誤,因為它沒有正確實現查找。
似乎是你的同事誤解了。 查找規則是:
注意:這些規則適用於自由函數,在類中不需要前向聲明
請注意,因為定義也充當聲明,所以在您的示例中,不必轉發聲明int
版本。
正確的例子:
template <typename T> void foo(T); // declare foo<T>
template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context)
template <> void foo<int>(int); // declare specialiaztion foo<int>
void bar(int i) { foo(i); } // instantiate foo<T> with int
// which is the specialization
如果有可用的基本模板,則這是一個錯誤。 如果在實例化之前未聲明特化,則不會使用它,並且這可能隨后意味着違反ODR規則(如果另一個實例化使用特化)。
從標准(C ++ 0x FDIS):
14.6.4.2
1.對於依賴於模板參數的函數調用,使用通常的查找規則(3.4.1,3.4.2,3.4.3)找到候選函數,除了:
- 對於使用非限定名稱查找(3.4.1)或限定名稱查找(3.4.3)的查找部分,僅找到模板定義上下文中的函數聲明。
- 對於使用關聯命名空間(3.4.2)的查找部分,僅找到在模板定義上下文或模板實例化上下文中找到的函數聲明。
如果函數名稱是非限定id並且調用將是格式錯誤或者找到更好的匹配,則相關命名空間內的查找會考慮所有在所有翻譯單元中的那些名稱空間中引入外部鏈接的函數聲明,而不僅僅是考慮在模板定義和模板實例化上下文中找到的那些聲明,則程序具有未定義的行為。
請注意,所提到的段落適用於常規功能。
兩階段查找將找到:
template <typename Head, typename... Tail> void foo (Head x, Tail... tail)
在ADL中找不到,所以如果在定義時看不到它就根本找不到。
換句話說,海灣合作委員會是對的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.