問(wèn)題描述
看看下面的代碼(只是為了好玩)
Check out the following code (written just for fun)
namespace N
{
template<typename T>
struct K
{
};
}
template<typename T>
struct X
{
typename T::template K<T> *p; //should give error
//N::K<int> has no template member named `K`
};
int main()
{
X<N::K<int> > l;
}
代碼在 g++(4.5.1) 和 Clang 上編譯,而 Comeau 和 Intel C++ 給出(類(lèi)似)錯(cuò)誤.
The code gets compiled on g++(4.5.1) and Clang whereas Comeau and Intel C++ give (similar) errors.
我在 Comeau 上遇到的錯(cuò)誤是:
The errors that I get on Comeau are :
"ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
typename T::template K<T> *p;
^
detected during instantiation of class "X<T> [with T=N::K<int>]" at
line 18
"ComeauTest.c", line 13: error: expected an identifier
typename T::template K<T> *p;
^
detected during instantiation of class "X<T> [with T=N::K<int>]" at
line 18
所以我的問(wèn)題是代碼示例格式錯(cuò)誤嗎?"據(jù)我說(shuō)是".這是否意味著這是 g++/Clang 中的另一個(gè)錯(cuò)誤?
So my question is "Is the code sample ill-formed ?" According to me "Yes". Does that mean this is yet another bug in g++/Clang?
推薦答案
為什么 GCC 和 Clang 認(rèn)為他們是對(duì)的
K
,即注入的類(lèi)名,在K
的范圍內(nèi)具有雙重性質(zhì).您可以在沒(méi)有模板參數(shù)的情況下使用它.然后它引用 K
(指向它自己的類(lèi)型).
Why GCC and Clang think they are right
K
, which is the injected class name, has a dual nature in the scope of K<int>
. You can use it without template arguments. Then it refers to K<int>
(to its own type).
它后面也可以跟一個(gè)模板參數(shù)列表.IMO 可以合理地說(shuō)您需要使用 template
作為前綴,因?yàn)榻馕銎髋c后面的 <
有歧義.然后它引用由模板參數(shù)確定的指定類(lèi)型.
It can be followed by a template argument list too. IMO it's reasonable to say that you need to prefix it with template
because of the parser ambiguity with the <
that follows. It then refers to the specified type that's determined by the template arguments.
因此可以將其視為成員模板和嵌套類(lèi)型,具體取決于它后面是否跟有模板參數(shù)列表.當(dāng)然,K
并不是真正的成員模板.盡管如此,注入的類(lèi)名的雙重性質(zhì)在我看來(lái)更像是一種黑客攻擊.
So it can be treated as a member template and as a nested type, depending on whether it's followed by a template argument list. Of course, K
is not really a member template. The dual nature of the injected class name seems to me more of a hack anyway, though.
標(biāo)準(zhǔn)有一個(gè)這樣的例子:
The Standard has an example that reads like this:
template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
typename Derived::Base b; // error: ambiguous
typename Derived::Base<double> d; // OK
};
人們可能傾向于由此得出結(jié)論,目的是您可以放棄模板
.標(biāo)準(zhǔn)說(shuō)
One might be inclined to conclude from this that the intent is that you could leave off the template
. The Standard says
對(duì)于要由模板參數(shù)顯式限定的模板名稱(chēng),必須知道該名稱(chēng)以引用模板.
For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.
我看不出這如何不適用于 T::K
.如果 T
是一個(gè)依賴(lài)類(lèi)型,那么你可以向后靠,因?yàn)樵诮馕鏊鼤r(shí)你不知道 K
指的是什么,所以為了理解代碼,你只需要能夠以 template
為前綴.請(qǐng)注意,n3225 也有這個(gè)例子,但它不是一個(gè)缺陷:如果你在 C++0x 中查找模板自己的范圍(它被稱(chēng)為當(dāng)前實(shí)例化"),你可以正式放棄 template
.
I can't see how this wouldn't apply to T::K<T>
. If T
is a dependent type then you can just lean back because you can't know what K
refers to when parsing it, so to make any sense of the code, you just have to be able to prefix it with template
. Notice that n3225 has that example too, but it's not a defect there: You can officially leave off template
if you lookup into the template's own scope in C++0x (it's called the "current instantiation").
所以到目前為止,Clang 和 GCC 都很好.
So until now, Clang and GCC are fine.
為了讓它更復(fù)雜,我們將不得不考慮 K
的構(gòu)造函數(shù).隱式聲明了一個(gè)默認(rèn)構(gòu)造函數(shù)和一個(gè)復(fù)制構(gòu)造函數(shù).名稱(chēng) K
將引用 K
的構(gòu)造函數(shù) 除非 使用的名稱(chēng)查找將忽略函數(shù)(構(gòu)造函數(shù))名稱(chēng).typename T::K
會(huì)忽略函數(shù)名嗎?3.4.4/3 說(shuō)明了詳細(xì)的類(lèi)型說(shuō)明符,其中 typename ...
是其中之一:
Just to make it even more complicated, we will have to consider the constructors of K<int>
. There is a default constructor and a copy constructor implicitly declared. A name K<int>::K
will refer to the constructor(s) of K<int>
unless the name lookup used will ignore function (constructor) names. Will typename T::K
ignore function names? 3.4.4/3 says about elaborated type specifiers, which typename ...
is one of:
如果名稱(chēng)是qualified-id,則根據(jù)其限定條件查找名稱(chēng),如3.4.3 所述,但忽略任何已聲明的非類(lèi)型名稱(chēng).
If the name is a qualified-id, the name is looked up according its qualifications, as described in 3.4.3, but ignoring any non-type names that have been declared.
然而,typename ...
使用不同的查找.14.6/4 說(shuō)
However, a typename ...
uses different lookup. 14.6/4 says
通常的限定名稱(chēng)查找 (3.4.3) 用于查找限定 ID,即使存在 typename 也是如此.
The usual qualified name lookup (3.4.3) is used to find the qualified-id even in the presence of typename.
3.4.3 的通常限定查找不會(huì)忽略非類(lèi)型名稱(chēng),如 14.6/4 所附示例所示.因此,我們將找到 3.4.3.1/1a 中指定的構(gòu)造函數(shù)(僅在 not 忽略非類(lèi)型時(shí)才會(huì)發(fā)生的附加扭曲是由后來(lái)的缺陷報(bào)告添加的,所有流行的 C++03 編譯器雖然實(shí)現(xiàn)):
The usual qualified lookup of 3.4.3 won't ignore non-type names, as illustrated by the example attached to 14.6/4. So, we will find the constructor(s) as specified by 3.4.3.1/1a (the additional twist that this only happens when non-types are not ignored was added by a later defect report, which all popular C++03 compilers implement though):
如果嵌套名稱(chēng)說(shuō)明符指定一個(gè)類(lèi) C,并且在嵌套名稱(chēng)說(shuō)明符后面指定的名稱(chēng)在 C 中查找時(shí)是 C 的注入類(lèi)名稱(chēng)(第 9 條),則名稱(chēng)為而是考慮命名類(lèi) C 的構(gòu)造函數(shù).這樣的構(gòu)造函數(shù)名稱(chēng)只能在出現(xiàn)在類(lèi)定義之外的構(gòu)造函數(shù)定義的聲明符中使用.
If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition that appears outside of the class definition.
所以最后,我認(rèn)為 comeau 的診斷是正確的,因?yàn)槟鷩L試將模板參數(shù)列表放在非模板上,并且還違反了最后一部分中引用的要求(您在其他地方使用了該名稱(chēng)).
So in the end, I think comeau is correct to diagnose this, because you try to put a template argument list onto a non-template and also violate the requirement quoted in the last part (you use the name elsewhere).
讓我們通過(guò)派生類(lèi)訪問(wèn)注入的名稱(chēng)來(lái)更改它,這樣就不會(huì)發(fā)生構(gòu)造函數(shù)名稱(chēng)轉(zhuǎn)換,并且您真的訪問(wèn)了類(lèi)型,以便您真的 可以附加模板參數(shù):
Let's change it by accessing the injected name by a derived class, so no constructor name translation occurs, and you really access the type so that you really can append the template arguments:
// just replace struct X with this:
template<typename T>
struct X
{
struct Derived : T { };
typename Derived::template K<T> *p;
};
現(xiàn)在所有東西都可以用 comeau 編譯了!請(qǐng)注意,我已經(jīng)向 clang 做了關(guān)于這件事的問(wèn)題報(bào)告.請(qǐng)參閱錯(cuò)誤的構(gòu)造函數(shù)名稱(chēng)解析.順便說(shuō)一句,如果你在 K
中聲明了一個(gè)默認(rèn)構(gòu)造函數(shù),如果你使用 T::K
Everything compiles now with comeau too! Notice I already did problem report to clang about this exact thing. See Incorrect constructor name resolution. BTW, if you declare a default constructor in K
, you can see comeau give a better error message if you use T::K<int>
"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
not a template
typename T::template K<T> *p;
這篇關(guān)于g++/Clang 中的另一個(gè)錯(cuò)誤?[C++ 模板很有趣]的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!