Форвардные объявления вызывают ошибки после рефакторинга кода

Моя оригинальная структура класса была похожа на:

//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 следует реорганизовать, чтобы приспособить новое пространство имен (это одно из решений), но, к сожалению, это не вариант.

Фактический код намного сложнее, это упрощенный пример.

5

Решение

Я понимаю, что это больше о новом коде, чем о рефакторинге существующего кода, но мне нравится использовать специальный заголовок под названием 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.

4

Другие решения

Лучший подход — это исправить код.

Вы можете сделать это в два этапа:

  1. исправить все вперед объявляет
  2. Удалить using ::B::X;
1

По вопросам рекламы [email protected]