类模板中名称解析的实际结果与 c++ 03标准
我在 Xcode 4.1 和 Visual Studio 2008 上测试了 C++ 标准 ISO/IEC 14882-03 14.6.1/9 中的代码。两个编译器的输出都与标准的预期结果不同。
代码粘贴在下面。
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
void f(int);
void h()
{
g(2);
g('a');
}
void f(int)
{
cout << "f int" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
正如标准的描述。预期输出应为“
f char
f int
f int
f char
f char
f char
在 Xcode 4.1 上构建并运行代码”。输出如下。在构建设置中,我尝试将“Compiler for C/C++/Object-C”更改为 Apple LLVM Compiler 2.1、Gcc 4.2 和 LLVM GCC 4.2。输出是相同的。
f char
f char
f char
f char
f char
f char
在 Microsoft Visual Studio 2008 上构建并运行代码。输出如下。
f int
f int
f int
f int
f char
f char
该标准的描述(14.6.1/9)粘贴在下面。
如果名称不依赖于模板参数(如 14.6.2 中定义),则该名称的声明(或声明集)应位于该名称出现在模板定义中的范围内;该名称绑定到在该点找到的声明(或多个声明),并且此绑定不受实例化点可见的声明的影响。 [示例:
void f(char);
template<class T> void g(T t)
{
f(1); // f(char)
f(T(1)); // dependent
f(t); // dependent
dd++; // not dependent
}
void f(int);
double dd;
void h()
{
// error: declaration for dd not found
g(2); // will cause one call of f(char) followed // by two calls of f(int)
g(’a’); // will cause three calls of f(char)
—结束示例]
对于编译器来说,代码格式良好,但输出不同。将此代码移植到不同平台将是非常危险的。
有人知道为什么这些编译器不遵循标准吗?
编辑于 10/11/2011
根据 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197,标准中的示例是错误的。我在 Clang 和 Gcc 上测试了下面的代码。
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
enum E{ e };
void f(E );
void h()
{
g(e);
g('a');
}
void f(E )
{
cout << "f E" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
输出如预期。
f char
f E
f E
f char
f char
f char
谢谢,
杰弗里
I test the code in the c++ standard ISO/IEC 14882-03 14.6.1/9 on Xcode 4.1 and Visual Studio 2008. The outputs of the two compiler are both different from the expected result of the standard.
The code is pasted below.
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
void f(int);
void h()
{
g(2);
g('a');
}
void f(int)
{
cout << "f int" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
As the description of the standard. The expected output should be
f char
f int
f int
f char
f char
f char
Build and run the code on Xcode 4.1. The output is as below. In the build settings, I tried to change the "Compiler for C/C++/Object-C" to be Apple LLVM Compiler 2.1, Gcc 4.2 and LLVM GCC 4.2. The outputs are the same.
f char
f char
f char
f char
f char
f char
Build and run the code on Microsoft Visual Studio 2008. The output is as below.
f int
f int
f int
f int
f char
f char
The description (14.6.1/9) of the standard is pasted below.
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declara- tions) for that name shall be in scope at the point where the name appears in the template definition; the name is bound to the declaration (or declarations) found at that point and this binding is not affected by declarations that are visible at the point of instantiation. [Example:
void f(char);
template<class T> void g(T t)
{
f(1); // f(char)
f(T(1)); // dependent
f(t); // dependent
dd++; // not dependent
}
void f(int);
double dd;
void h()
{
// error: declaration for dd not found
g(2); // will cause one call of f(char) followed // by two calls of f(int)
g(’a’); // will cause three calls of f(char)
—end example]
The code is well-formed to the compilers, but the outputs are different. It would be very dangerous to port this code to different platforms.
Does somebody have the background why these compilers don't follow the standard?
Edit on 10/11/2011
Per http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#197, the example in the standard is wrong. I test the code below on Clang and Gcc.
#include <stdio.h>
#include <iostream>
using namespace std;
void f(char);
template <class T > void g(T t)
{
f(1);
f(T(1));
f(t);
}
enum E{ e };
void f(E );
void h()
{
g(e);
g('a');
}
void f(E )
{
cout << "f E" << endl;
}
void f(char)
{
cout << "f char" << endl;
}
int main() {
h();
return 0;
}
The output as expected.
f char
f E
f E
f char
f char
f char
Thanks,
Jeffrey
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您遇到的问题是 Visual Studio 没有实现 两阶段查找。他们仅在实例化模板时查找实际名称。
微软此时已经决定他们对支持两阶段查找不感兴趣。
What you're running into is the fact that Visual Studio does not implement two-phase lookup. They only look up the actual name when you instantiate the template.
And Microsoft has pretty much decided at this point that they're not interested in supporting two-phase lookup.
正如第一个示例中所指出的,这是一个两阶段名称查找的实例,GCC 和 Clang 都实现了,但 MSVC 没有。在这种情况下,GCC 和 Clang 都是正确的:实际上是错误的标准,如 C++ 核心缺陷报告#197。 C++11 标准包含一个不同的示例。
这是我们在将代码从 MSVC (从未实现过两阶段名称查找)或来自 GCC(直到最近才统一实现两阶段名称查找)。
As noted in the first example, this is an instance of two-phase name lookup, which both GCC and Clang implement but MSVC does not. And in this case, both GCC and Clang are correct: it's actually the standard that is wrong, as noted in C++ core defect report #197. The C++11 standard contains a different example.
This is one of the most common problems we see when porting code to Clang from either MSVC (which never implemented two-phase name lookup) or from GCC (which didn't implement two-phase name lookup uniformly until recently).
我不知道该告诉你什么,除了我同意你的观点,这是不正确的行为。
我认为可能发生的情况是,在 MSVC 的情况下,编译器正在优化额外的传递,但代价是最终了解了稍后定义的函数,而该函数在非模板调用的情况下不应使用。我必须承认,我不明白 GCC/LLVM 如何最终得到他们所做的结果,因为结果是你所期望的例外而不是规则。
我想我会将其作为 http://bugreport.apple.com/ 和 http://connect.microsoft.com/ 看看他们说什么?
I don't know what to tell you except that I would agree with you that this is incorrect behavior.
I think what's probably happening is that in the case of MSVC, the compiler is optimizing away an extra pass at the cost of ending up with the knowledge of a later-defined function that should not be use in the case of non-template calls. I must confess, I don't get how GCC/LLVM would end up with the results that they do, as the results are what you would expect as the exception and not the rule.
I guess I'd file it as a bug on http://bugreport.apple.com/ and http://connect.microsoft.com/ and see what they say?