Компилятору C ++ не удалось найти функцию (связанную с пространством имен)

StackOverflow https://stackoverflow.com/questions/493985

Вопрос

Я работаю в 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 (только специализации шаблонов).

Конец оригинального поста.

Даже если, как было прокомментировано ранее, это может быть просто проблемой с компилятором, это обычное явление, и рекомендуется определять все свободные функции, которые работают с пользовательским типом, в том же пространстве имен, что и сам тип.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top