Эта проблема с областями видимости напоминает проблему С ++, о которой Скотт Мейерс мог бы рассказать в одной из своих Эффективных С ++ книг.
У меня есть функция Analyze
, которая выполняет некоторый анализ диапазона данных. Функция вызывается из нескольких мест с различными типами итераторов, поэтому я сделал ее шаблоном (и таким образом реализовал его в заголовочном файле). Функция зависит от статической таблицы данных AnalysisTable
, которую я не хочу показывать остальной части кода.
Мой первый подход состоял в том, чтобы сделать таблицу static const
внутри Analysis
.
namespace MyNamespace {
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
static const int AnalysisTable[] = { /* data */ };
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
Похоже, что компилятор создает копию AnalysisTable
для каждого экземпляра Analyze
, что приводит к расточительству пространства (и, в небольшой степени, времени).
Поэтому я переместил таблицу за пределы функции следующим образом:
namespace MyNamespace {
const int AnalysisTable[] = { /* data */ };
template <typename InputIterator>
int Analyze(InputIterator begin, InputIterator end) {
... // implementation uses AnalysisTable
return result;
}
} // namespace MyNamespace
Сейчас есть только одна копия таблицы, но она доступна для остальной части кода. Я бы предпочел скрыть эту деталь реализации, поэтому я ввел безымянное пространство имен:
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
Но теперь у меня снова есть несколько копий таблицы, потому что каждый модуль компиляции, который включает этот заголовочный файл, получает свою собственную. Если бы Analyze
не был шаблоном, я мог бы переместить все детали реализации из файла заголовка. Но это шаблон, поэтому я, кажется, застрял.
Моей следующей попыткой было поместить таблицу в файл реализации и сделать объявление extern
в 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 */ };
}
Похоже, это должно работать, и компилятор удовлетворен. Однако компоновщик жалуется: «неразрешенный внешний символ AnalysisTable
». Убирайся! (Может кто-нибудь объяснить, что мне здесь не хватает?)
Единственное, о чем я мог подумать, - это дать внутреннему пространству имен имя, объявить таблицу в заголовке и предоставить фактические данные в файле реализации:
// 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 */ };
}
}
Еще раз, у меня есть ровно один экземпляр AnalysisTable
(ууу!), Но другие части программы могут получить к нему доступ (бу!). Внутреннее пространство имен делает немного более ясным, что они не должны , но это все еще возможно.
Можно ли иметь один экземпляр стола и перемещать стол за пределы досягаемости всего, кроме Analyze
?