我们正在将我们的XL C / C ++编译器从V8.0升级到V10.1,并发现一些代码现在给我们一个错误,即使它是在V8.0下编译的。这是一个最小的例子:
test.h:
#include <iostream>
#include <string>
template <class T>
void f()
{
std::cout << TEST << std::endl;
}
TEST.CPP:
#include <string>
#include "test.h"
namespace
{
std::string TEST = "test";
}
int main()
{
f<int>();
return 0;
}
在V10.1下,我们收到以下错误:
"test.h", line 7.16: 1540-0274 (S) The name lookup for "TEST" did not find a declaration.
"test.cpp", line 6.15: 1540-1303 (I) "std::string TEST" is not visible.
"test.h", line 5.6: 1540-0700 (I) The previous message was produced while processing "f<int>()".
"test.cpp", line 11.3: 1540-0700 (I) The previous message was produced while processing "main()".
我们发现g ++ 3.3.2和4.3.2之间存在类似的差异。我也发现在g ++中,如果我将#include "test.h"
移动到未命名的命名空间声明之后,编译错误就会消失。
所以这是我的问题:标准对此有何看法?在实例化模板时,该实例是否被认为是在声明模板本身的位置声明的,或者是在这一点上不明确的标准?我做了一些关于n2461.pdf草案的看法,但没有真正想出任何确定的东西。
答案 0 :(得分:5)
这不是有效的C ++代码。 TEST
不依赖于模板参数T
,因此必须在解析模板定义的上下文中找到它。但是,在该上下文中,不存在TEST
的声明,因此存在错误。
该格式错误的模板的诊断消息可以延迟到编译器实例化,但如果编译器是好的,它将更早地诊断错误。即使在实例化模板时,也没有为该代码提供任何诊断消息的编译器也不符合要求。它与未命名的命名空间无关。
此外,请注意,即使您将未命名的命名空间放在该模板上方,如果您在多个翻译单元中定义并调用该模板,它也将不是有效的C ++程序。这是因为具有相同模板参数的相同模板的不同实例化将引用不同的事物(未命名的命名空间中的字符串将在每次在另一个转换单元中定义时产生不同的对象)。这种程序的行为将是不确定的。
答案 1 :(得分:0)
请记住,#include只是将.h文件的内容复制到.cpp文件中。因此,您对f()的定义出现在TEST的定义之前。解决这个问题的最佳方法是添加
extern std::string TEST;
位于.h文件的顶部。
答案 2 :(得分:0)
此测试未通过Comeau online compiler,而{{3}}过去已证明是最符合标准的编译器之一。我倾向于支持,然后,代码是不正确的,尽管我不能指出你在标准中为什么。请注意,以宽松模式编译代码会成功。