Моя оригинальная структура класса была похожа на:
//def.h
namespace A
{
struct X {};
}
и направлять декларации, где это необходимо:
//file that needs forward declarations
namespace A { struct X; }
После некоторого рефакторинга, X
был перемещен в другое пространство имен, но чтобы старый код работал using
Директивы были использованы:
//def.h
namespace B
{
struct X {};
}
namespace A
{
using ::B::X;
}
Теперь мы можем получить доступ к тому же классу, сохраняя старый синтаксис A::X
, но предварительные объявления вызывают ошибки. Вторая проблема заключается в том, что сообщение об ошибке, которое я получаю, не указывает на то, где находятся предварительные объявления, а поиск / замена предварительных объявлений занимает много времени.
Пока я исправил проблему (трудный путь).
Каков наилучший подход к решению этой ситуации?
ИМО, using
не должно быть там вообще, и весь код, который использует X
следует реорганизовать, чтобы приспособить новое пространство имен (это одно из решений), но, к сожалению, это не вариант.
Фактический код намного сложнее, это упрощенный пример.
Я понимаю, что это больше о новом коде, чем о рефакторинге существующего кода, но мне нравится использовать специальный заголовок под названием X_fwd.hpp
для таких случаев.
// X_def.hpp
namespace B
{
struct X {};
}
namespace A
{
// NOT: using namespace B; // does not participate in ADL!
typedef ::B::X X; // OR: using ::B::X;
}
// X_fwd.hpp
namespace A { struct X; }
// some file needing declaration of X
#include <X_fwd.hpp>
Это значительно облегчает поиск предварительных деклараций, а также их изменение после факта, потому что изменение изолировано только в одном месте (СУХОЙ …).
NOTE1: AFAIK, нет никакой технической разницы между использованием ответа Питера Вуда typedef
и ваш using
декларация. Обратите внимание, что using
директива using namespace B;
может вызвать проблемы, потому что они игнорируются Аргумент-Dependent-Lookup. Что еще хуже, часть вашего кода может даже молча вызвать неправильную функцию перегрузки потому что вы не тянете в новом пространстве имен B
больше!
ЗАМЕТКА 2: В комментариях к вопросу Ideone пример был приведен. Это прекрасно иллюстрирует тонкость поиска имен в пространствах имен: цитата из черновика стандарт, раздел 3.4.3.2 Члены пространства имен [namespace.qual], пункт 2
Для пространства имен X и имени m, квалифицированный набор поиска пространства имен S (X,
m) определяется следующим образом: пусть S ‘(X, m) будет множеством всех объявлений
m в X и встроенный набор пространств имен X (7.3.1). Если S ‘(X, m)
не пустой, S (X, m) есть S ‘(X, m); в противном случае S (X, m) является объединением
S (Ni, m) для всех пространств имен Ni, назначенных директивами использования в X и
его встроенное пространство имен.
Это объясняет следующую хитрую двусмысленность
namespace A
{
struct X1{};
struct X2{};
}
namespace B
{
using A::X1; // OK: lookup-set is both namespace A and B, and a single unique name is found (at this point!)
struct X1; // OK: lookup-set is namespace B, and a single unique name is found
struct X2; // OK: lookup-set is namespace B, and a single unique name is found
using A::X2; // error: lookup-set is both namespace A and B, and no unique name is found (at this point!)
}
Таким образом, допустимость наличия как прямого объявления, так и объявления об использовании с одним и тем же именем в пространстве имен зависит от порядка. Отсюда удобство одного объявления в заголовочном файле fwd.
Лучший подход — это исправить код.
Вы можете сделать это в два этапа:
using ::B::X;