Scope quandary with namespaces, function templates, and static data
- by Adrian McCarthy
This scoping problem seems like the type of C++ quandary that Scott Meyers would have addressed in one of his Effective C++ books.
I have a function, Analyze, that does some analysis on a range of data. The function is called from a few places with different types of iterators, so I have made it a template (and thus implemented it in a header file). The function depends on a static table of data, AnalysisTable, that I don't want to expose to the rest of the code.
My first approach was to make the table a static const inside Analysis.
namespace MyNamespace {
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
static const int AnalysisTable[] = { /* data */ };
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
It appears that the compiler creates a copy of AnalysisTable for each instantiation of Analyze, which is wasteful of space (and, to a small degree, time).
So I moved the table outside the function like this:
namespace MyNamespace {
const int AnalysisTable[] = { /* data */ };
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
There's only one copy of the table now, but it's exposed to the rest of the code. I'd rather keep this implementation detail hidden, so I introduced an unnamed namespace:
namespace MyNamespace {
namespace { // unnamed to hide AnalysisTable
const int AnalysisTable[] = { /* data */ };
} // unnamed namespace
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
But now I again have multiple copies of the table, because each compilation unit that includes this header file gets its own. If Analyze weren't a template, I could move all the implementation detail out of the header file. But it is a template, so I seem stuck.
My next attempt was to put the table in the implementation file and to make an extern declaration within Analyze.
// foo.h ------
namespace MyNamespace {
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
extern const int AnalysisTable[];
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
// foo.cpp ------
#include "foo.h"
namespace MyNamespace {
const int AnalysisTable[] = { /* data */ };
}
This looks like it should work, and--indeed--the compiler is satisfied. The linker, however, complains, "unresolved external symbol AnalysisTable." Drat! (Can someone explain what I'm missing here?)
The only thing I could think of was to give the inner namespace a name, declare the table in the header, and provide the actual data in an implementation file:
// foo.h -----
namespace MyNamespace {
namespace PrivateStuff {
extern const int AnalysisTable[];
} // unnamed namespace
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
... // implementation uses PrivateStuff::AnalysisTable
return result;
}
} // namespace MyNamespace
// foo.cpp -----
#include "foo.h"
namespace MyNamespace {
namespace PrivateStuff {
const int AnalysisTable[] = { /* data */ };
}
}
Once again, I have exactly one instance of AnalysisTable (yay!), but other parts of the program can access it (boo!). The inner namespace makes it a little clearer that they shouldn't, but it's still possible.
Is it possible to have one instance of the table and to move the table beyond the reach of everything but Analyze?