Почему я не могу определить объявленный вперед класс, назвав его псевдонимом?

Попытка скомпилировать приведенный ниже код не удалась из-за «конфликтующей декларации». Почему я не могу определить объявленный вперед класс, подобный этому?

Я сделал это, чтобы скрыть, что реализация использует некую библиотеку. Хотя я признаю, что на самом деле это ничего не абстрагирует — вам все равно нужно знать, что использует реализация для выполнения правильного вызова — мне все еще интересно, почему это не работает.

Bar.cpp:

#include "Bar.hpp"
#include "Foo.hpp"

using Foo = ns::Foo;

void Bar::foo(Foo f) {
}

Bar.hpp:

class Foo;

class Bar {
    void foo(Foo f);
};

Foo.hpp:

namespace ns {

    class Foo {
    };
}

Чтобы было ясно, я хочу знать, почему нельзя определить ранее объявленный класс с помощью псевдонима - другими словами, говоря: «используйте это определение там, которое имеет другое имя»


person Oebele    schedule 17.03.2016    source источник
comment
в Bar.hpp поместите предварительное объявление Foo в правильное пространство имен. И это должно работать   -  person Slava    schedule 17.03.2016
comment
@Слава, который побеждает. Также я попытался скомпилировать его: ошибка: «Foo» не был объявлен   -  person Oebele    schedule 17.03.2016
comment
@Слава, плохо, я забыл указать Foo в аргументе функции.   -  person Oebele    schedule 17.03.2016


Ответы (2)


Вы объявляете Foo дважды с конфликтующими типами. Во-первых, вы объявляете Foo в Bar.hpp как:

class Foo;

Впоследствии вы объявляете foo в Bar.cpp как:

using Foo = ns::Foo;

Вы не можете поместить такое предварительное объявление, если вы определяете в исходном файле псевдоним с тем же именем, потому что тогда вы объявляете два разных типа с точно таким же именем.

Основываясь на вашем вопросе, я предполагаю, что вы хотите использовать Foo без его пространства имен в Bar.cpp. Решение выглядит следующим образом:

Бар.cpp

#include "Bar.hpp"
#include "Foo.hpp"

using ns::Foo;

void Bar::foo(Foo f) {}

Бар.хпп

namespace ns
{
    class Foo;
}

class Bar 
{
    void foo(ns::Foo f);
};

Foo.hpp

namespace ns
{
    class Foo
    {};
}
person Chiel    schedule 17.03.2016
comment
Почему типы конфликтуют? Оба являются классами, и ::Foo не был определен до его определения с помощью оператора using. Обратите внимание, что я хочу знать, почему это не работает, больше, чем интерес к исправлению. - person Oebele; 17.03.2016
comment
Сначала вы создаете typedef, в котором говорится, что Foo равно ns::Foo. Позже вы повторно объявляете Foo как класс в глобальном пространстве имен. Так что же такое Foo? Просто Foo или ns::Foo?. Я не знаю, и компилятор тоже. - person Chiel; 17.03.2016
comment
Ах, я не знал, что это считается typedef. У меня сейчас есть аналогичный случай, когда библиотека предоставляет конкретную версию класса шаблона через оператор using. Как я могу объявить это? - person Oebele; 17.03.2016
comment
Упреждающее объявление не является проблемой, если вы не опускаете пространство имен, потому что они становятся разными объявлениями, сделанными с одним и тем же именем, что приводит к конфликтам. - person Chiel; 17.03.2016
comment
Ваш ответ читается немного задом наперед - вы говорите, что Bar.cpp произошел до Bar.h. - person Barry; 17.03.2016

Потому что для того, чтобы Bar::Foo работал, класс Bar должен знать размер аргумента Foo еще до его объявления. Вы можете заставить это работать, объявив void foo(Foo* f); Таким образом, вместо того, чтобы знать размер Foo, компилятор будет заботиться только о размере указателя. Просто используйте, чтобы выбрать нужное пространство имен:

using namespace ns;
person Dimo Markov    schedule 17.03.2016
comment
Но это не решает проблему пространства имен, о которой упоминает Обеле. - person Chiel; 17.03.2016
comment
Почему бы просто не использовать: using namespace ns; вместо using Foo = ns::Foo; - person Dimo Markov; 17.03.2016
comment
@DimoMarkov Возможно, я не захочу использовать все пространство имен, например, из-за конфликтов имен. Особенно, когда этот класс Foo — единственный, который я часто использую из большого пространства имен. - person Oebele; 17.03.2016
comment
Тогда вы можете сделать то, что я предлагаю: using ns::Foo;. Это извлекает только Foo, а не остальную часть пространства имен ns. - person Chiel; 17.03.2016
comment
Как упомянул @Chiel в своем ответе, вы должны предварительно объявить Foo в пространстве имен, которое вы хотели бы использовать. Классу Bar требуется информация о типе Foo, чтобы объявить функцию-член. Без предоставления этой информации в заголовке ваша реализация не будет синхронизирована, и, следовательно, у вас возникнет конфликт. Если вы не объявите Bar::foo с аргументом Foo*, как я упоминал в своем ответе. - person Dimo Markov; 17.03.2016
comment
@Chiel, конечно, я просто хотел указать на ошибку, связанную с извлечением всего пространства имен в ответ на вопрос Димо, почему бы просто не использовать: using namespace ns; вместо using Foo = ns::Foo; - person Oebele; 17.03.2016
comment
@Обеле. Это не то же самое. Первый делает функции в пространстве имен доступными в рамках оператора, тогда как второй — это typedef нового типа с этим конкретным именем, которое не равно написанию using ns::Foo; - person Chiel; 17.03.2016