Компилятор C ++ не может найти функцию (связано с пространством имен) - PullRequest
2 голосов
/ 30 января 2009

Я работаю в 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
      }
}

Содержимое MAIN-NAMESPACE разделено на несколько файлов, и по некоторым причинам, которые я не понимаю, оператор << для Set и LinkedList полностью находится вне MAIN-NAMESPACE (но в заголовке Set и LinkedList файл). Вот версия Set: </p>

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» (или нет приемлемого преобразования) </p>

Однако, если я просто напишу функцию main () и создам Set A, заполню ее и использую оператор - все работает.

Есть идеи, в чем проблема? (примечание: я пробовал любую комбинацию использования и включения, о которой мог подумать).

Ответы [ 8 ]

2 голосов
/ 30 января 2009

ОК, я понял это. Интуиция jpalecek о существовании другого оператора << в пространстве имен была правильной (очевидно, я забыл это закомментировать). </p>

Правила поиска для пространств имен сначала запускают поиск в пространстве имен вызова функции и осуществляют поиск по включенным пространствам имен, вплоть до глобального пространства имен (затем он выполняет поиск, зависящий от аргумента, если совпадение не найдено). Однако, если по пути он находит какое-либо совпадение для оператора <<, он останавливает поиск, независимо от того, что типы, используемые в этих функциях, могут быть несовместимыми, как это было здесь. </p>

Решение - либо включить его в MAIN-NAMESPACE (что мне запрещено), либо импортировать его из глобального пространства имен с помощью «using :: operator <<». </p>

2 голосов
/ 30 января 2009

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

Единственное, о чем я могу думать, это то, что объявление с тем же именем в 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);
  }
}
0 голосов
/ 30 января 2009

Это работает для меня

#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);
}
0 голосов
/ 30 января 2009

ОК, люди попросили конкретные примеры, так что вот соответствующая часть кода. // Disclamer: в тонком случае кто-то из моего универа видит это, находит это в файле представления и решает, что я скопировал или что-то еще, мой номер студента 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;
    }
0 голосов
/ 30 января 2009

Попробуйте вызвать функцию явно?

:: operator << (cout, myObj); </p>

Да, это работает!

попытается найти функцию f в текущее пространство имен (в месте вызов) или во вложенных пространствах имен типов c1 и c2 (namespace1, namespace2 :: namespace3), но это будет не пытайтесь использовать другие пространства имен в поиск.

Итак, давайте посмотрим, правильно ли я понял: причина вызова оператора << из функции main () заключается в том, что я находился в глобальном пространстве имен (как и оператор <<). Причиной сбоя при вызове из реализованного мною класса является то, что класс находился в неглобальном пространстве имен и в нем не было переменных, которые указывали бы компилятору на глобальное пространство имен. </p>

0 голосов
/ 30 января 2009

ИСПРАВЛЕНИЕ : текст ниже основан на опыте работы с компиляторами семейства g ++. После комментария к ответу я перечитал стандарт (который гласит, что ADL будет использоваться поверх обычного поиска имени, а обычный поиск имени должен найти оператор <<). Я также попробовал с <a href="http://www.comeaucomputing.com/" rel="nofollow noreferrer"> comeau компилятором (самый стандартный совместимый компилятор, который я знаю), и символ найден. Это похоже на проблему с 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 (namespace1, namespace2 :: namespace3), но он будет не пытайтесь использовать другие пространства имен в поиске.

(*) В этом случае вы в значительной степени ограничены пространством имен test , поскольку вам не разрешено добавлять функцию в пространство имен std (только для специализаций шаблонов).

Конец исходного сообщения .

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

0 голосов
/ 30 января 2009

Как указал SoaBox, попробуйте вызвать его явно.

К вашему сведению, если вы хотите вызвать глобальную функцию, которая была скрыта в текущем пространстве имен, предшествуйте функции с ::, чтобы обойти локальную функцию и вызвать глобальную функцию.

0 голосов
/ 30 января 2009

Попробуйте вызвать функцию явно?

::operator<<( cout, myObj );
...