We are upgrading our XL C/C++ compiler from V8.0 to V10.1 and found some code that is now giving us an error, even though it compiled under V8.0. Here's a minimal example:
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;
}
Under V10.1, we get the following error:
"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()".
We found a similar difference between g++ 3.3.2 and 4.3.2. I also found in g++, if I move the #include "test.h"
to be after the unnamed namespace declaration, the compile error goes away.
So here's my question: what does the Standard say about this? When a template is instantiated, is that instance considered to be declared at the point where the template itself was declared, or is the standard not that clear on this point? I did some looking though the n2461.pdf draft, but didn't really come up with anything definitive.
This is not valid C++ code. TEST
is not dependent on the template parameter T
, so it must be found in the context of the template definition when it's parsed. However, in that context no declaration of TEST
exists, and so there is an error.
The diagnostic message for that ill-formed template can be delayed until instantiation by the compiler, but if the compiler is good, it will diagnose the error earlier. Compilers that don't give any diagnostic message for that code even when the template is instantiated are not conforming. It has nothing to do with unnamed namespaces.
In addition, notice that even if you put the unnamed namespace above that template, it will not be a valid C++ program either if you define and call that template in multiple translation units. This is because different instantiations of the same template with the same template arguments will refer to different things (the string in the unnamed namespace will produce a different object each time it's defined in another translation unit). Behavior for such a program would be undefined.
Remember that #include simply copies the content of the .h file into the .cpp file. So, your definition of f() appears before the definition of TEST. The best way to get around this is to add
extern std::string TEST;
at the top of your .h file.
This test fails the Comeau online compiler which in the past has shown to be one of the most standards-compliant compilers around. I would lean in favor, then, that the code is incorrect as written, though I could not point you to a line in the standard as to why. Note that compiling the code in relaxed mode succeeds, however.