Компилятору C ++ не удалось найти функцию (связанную с пространством имен)
-
20-08-2019 - |
Вопрос
Я работаю в Visual Studio 2008 над заданием по программированию на C ++.Нам были предоставлены файлы, которые определяют следующую иерархию пространств имен (имена приведены только ради этого поста, я знаю, что "пространство имен XYZ-NAMESPACE" является избыточным):
(MAIN-NAMESPACE){
a bunch of functions/classes I need to implement...
(EXCEPTIONS-NAMESPACE){
a bunch of exceptions
}
(POINTER-COLLECTIONS-NAMESPACE){
Set and LinkedList classes, plus iterators
}
}
Содержимое ОСНОВНОГО пространства имен разделено между кучей файлов, и по какой-то причине, которую я не понимаю, оператор<< как Set, так и LinkedList полностью находятся за пределами ОСНОВНОГО ПРОСТРАНСТВА имен (но внутри заголовочного файла Set и LinkedList).Вот установленная версия:
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set<T>& set)
Теперь вот в чем проблема:У меня есть следующая структура данных:
Set A
Set B
Set C
double num
Он определен как находящийся в классе внутри MAIN-NAMESPACE .Когда я создаю экземпляр класса и пытаюсь распечатать один из наборов, он сообщает мне, что:ошибка C2679:двоичный файл '<<' :не найден оператор, который принимает правый операнд типа 'const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set' (или нет приемлемого преобразования)
Однако, если я просто напишу функцию main() и создам Set A, заполню ее и использую оператор - это сработает.
Есть какие-нибудь идеи, в чем проблема?(примечание:Я пробовал любую комбинацию использования и включения, какую только мог придумать).
Решение 2
Хорошо, я с этим разобрался.интуиция джпалечека о существовании другого оператора<< в пространстве имен было указано правильно (видимо, я забыл это прокомментировать).
Правила поиска для пространств имен сначала запускают поиск в пространстве имен вызова функции и выполняют поиск по охватывающим пространствам имен вплоть до глобального пространства имен (затем выполняется поиск, зависящий от аргумента, если совпадение не найдено).Однако, если по пути он найдет какое-то соответствие для operator<<, это останавливает поиск, независимо от того факта, что типы, используемые в этих функциях, могут быть несовместимы, как это было в данном случае.
Решение состоит либо в том, чтобы включить его в ОСНОВНОЕ пространство имен (что мне не разрешено), либо импортировать его из глобального пространства имен с помощью "using ::operator<<".
Другие советы
Странно - даже несмотря на то, что перенос свободных функций, связанных с типом, в другое пространство имен является плохой практикой, объявления глобального пространства имен всегда видны.
Единственное, о чем я могу думать, это о том, что объявление с тем же именем в MAIN-NAMESPACE
затенит то, что находится в глобальном пространстве имен - разве нет operator<<
, возможно, для совершенно не связанного типа, в MAIN-NAMESPACE
?Если это так, вы должны исправить это с помощью using ::operator<<
декларация в MAIN-NAMESPACE
.Пример:
namespace A
{
namespace B
{
class C{};
}
}
void f(A::B::C*);
namespace A
{
void f(int*); // try commenting
using ::f; // these two lines
void g()
{
B::C* c;
f(c);
}
}
Попробуйте вызвать функцию явно?
::operator<<( cout, myObj );
Как указал SoaBox, попробуйте вызвать его явно.
К вашему сведению, если вы хотите вызвать глобальную функцию, которая была скрыта в текущем пространстве имен, перед функцией укажите ::чтобы обойти локальную функцию и вызвать глобальную функцию.
Попробуйте вызвать функцию явно?
::оператор<<( cout, myObj );
Да, это действительно работает!
он попытается найти функцию f в текущем пространстве имен (в месте вызова ) или во вложенных пространствах имен типов c1 и c2 (namespace1, namespace2::namespace3), но он не будет пробовать другие пространства имен в процессе поиска.
Так что давайте посмотрим, правильно ли я все понял:причина вызова оператора<< из функции main() сработало это потому, что я был в глобальном пространстве имен (как и operator<<).Причина, по которой произошел сбой при вызове из класса, который я реализовал, заключается в том, что класс находился в неглобальном пространстве имен и в нем не было переменных, которые указывали бы компилятору на глобальное пространство имен.
Хорошо, люди просили конкретные примеры, так что вот соответствующая часть кода.//Разглашатель:в самом крайнем случае, кто-то из моего университета увидит это, обнаружит в файле отправки и решит, что я его скопировал или что-то в этом роде, мой номер студента 311670137
Это заголовочный файл Set.h:
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
(ITERATORS AND PREDICATE CLASSES)
template<typename T>
class Set {
public:
/////////////////////////////////
// Definitions
/////////////////////////////////
private:
/////////////////////////////////
// Definitions
/////////////////////////////////
};
///////////////////////////////////////////////////////////////////////////////
// The implementation part.
///////////////////////////////////////////////////////////////////////////////
}
}
// operator<< - the same a Set::print(std::ostream& os,
// const BinaryPredicate<T>& predicate)
// function called with the 'predicate' parameter omitted
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
set.print(os);
return os;
}
Это то, что я определил в другом файле:
namespace MTM {
using std::ostream;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<RegisteredStation> places;
Set<BusLine> busses;
Set<TrainLine> trains;
double tarifForBuses;
double tarifForTrains;
};
}
И вот из главного:
Schedule s();
s.registerStation("1");
s.reportRegisteredStations(cout);//This invokes the error. Definition follows:
reportRegisteredStations определяется как:
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
Это работает для меня
#include <iostream>
#include <string>
using std::string;
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
template<typename T>
class Set {
};
}
}
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
return os;
}
namespace MTM {
using std::ostream;
using PointerCollections::Set;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<int> places;
Set<int> busses;
Set<int> trains;
double tarifForBuses;
double tarifForTrains;
};
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
}
int main()
{
MTM::Schedule s;
s.reportRegisteredStations(std::cout);
}
ИСПРАВЛЕНИЕ:Приведенный ниже текст основан на опыте работы с компиляторами семейства g ++.После комментария к ответу я перечитал стандарт (в котором говорится, что ADL будет использоваться поверх обычного поиска по имени, а обычный поиск по имени должен найти оператора<<).Я также пробовал с комо компилятор (самый стандартный компилятор, который я знаю), и символ найден.Похоже, это проблема с g ++ (пробовал версии 3.3, 4.1, 4.3).
Оригинальный ответ:
Поиск по запросу Кенинга (технически ADL:Поиск, зависящий от аргумента).
Короткий ответ заключается в том, что если у вас есть следующий класс:
namespace test {
class A {};
}
оператор вставки потока должен быть определен как:
namespace test {
std::ostream& operator<<( std::ostream&, A const & );
}
Функции или операторы должны быть определены в том же пространстве имен, что и один из аргументов, которые он принимает.(*)
Когда компилятор находит вызов функции, такой как:
namespace test2 {
void g() {
namespace1::class1 c1;
namespace2::namespace3::class2 c2;
f( c1, c2 );
}
}
он попытается найти f функция в текущем пространстве имен (в месте вызова) или во входящих пространствах имен типов c1 и c2 (пространство имен1, пространство имен2::пространство имен3), но она не будет пробовать другие пространства имен при поиске.
(*) В этом случае вы в значительной степени ограничены тест пространство имен, поскольку вам не разрешено добавлять функцию в пространство имен std (только специализации шаблонов).
Конец оригинального поста.
Даже если, как было прокомментировано ранее, это может быть просто проблемой с компилятором, это обычное явление, и рекомендуется определять все свободные функции, которые работают с пользовательским типом, в том же пространстве имен, что и сам тип.