views:

77

answers:

3

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.

A: 

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.

Artem
A: 

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.

fbrereto
+4  A: 

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.

Johannes Schaub - litb
Thanks, Johannes. I was hoping you might chime in. I find it interesting that the older compilers passed it. Is there an area of the Standard I could read to understand this better?
Fred Larson
@Fred the compiler seems to have improved to do two-phase name lookup now. See the template faq: http://womble.decadentplace.org.uk/c++/template-faq.html
Johannes Schaub - litb
Regarding your edit, I thought of that as well. I think our resolution of this problem will be to eliminate such references from the templates. That will be more correct behavior anyway.
Fred Larson
Thanks for the link. I'll look it up when I get home. Our Internet filter at work won't let me go there. >8v(
Fred Larson
I think I found this in 14.6.9 of the C++03 draft standard (n2461.pdf).
Fred Larson
@Fred, yes that's the rule in 14.6/9.
Johannes Schaub - litb
The change in behavior for non-dependent names, among other things, in g++ 3.4 is documented here: http://gcc.gnu.org/gcc-3.4/changes.html. Search for "non-dependent".
Void
Man, you are talking C++ standard. Well deserved +1.
Tomek