Проблемы дружбы при переопределении оператора << - PullRequest
1 голос
/ 06 ноября 2011

Я пытаюсь перегрузить operator<< стандартным способом. У меня есть класс с именем SymbolTable, находящийся в файле с именем SymbolTable.h следующим образом:

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table);
private:
  vector<map<int, Symbol*> > mSymbols;
};

// ...

}

Реализация operator<< обеспечивает доступ к некоторым закрытым членам SymbolTable. Вот подпись реализации, чтобы продемонстрировать, что она не отличается от подписи предварительного объявления (я скопировал ее, чтобы быть уверенным, что не схожу с ума.)

ostream& operator<<(ostream &out, const SymbolTable &table)
{ ... }

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

ld: дубликат символа Compaler :: operator << (std :: basic_ostream> &, Compaler :: SymbolTable const &) в /var/folders/kt/pl6qd1490fn3yqxfpg64jyr80000gn/T//ccstrYnU.o и / var / folder / kt /pl6qd1490fn3yqxfpg64jyr80000gn/T//ccDQFiyK.o для архитектуры x86_64

Однако, если я вместо этого положу его в SymbolTable.cpp, где находятся остальные реализации моего члена SymbolTable, код даже не скомпилируется; Я получаю сообщения об ошибках при доступе к личным пользователям, указывающие, что дружба не распознается:

.. / SymbolTable / SymbolTable.h: В функции ‘std :: ostream & operator << (std :: ostream &, const Compaler :: SymbolTable &) ’: ../SymbolTable/SymbolTable.h:75: ошибка: 'std :: vector, std :: allocator>, Compaler :: Symbol *, std :: less, std :: allocator>>, std :: allocator, std: : allocator>, Compaler :: Symbol *>>>, std :: allocator, std :: allocator>, Compaler :: Symbol *, std :: less, std :: allocator>>, std :: allocator, std :: allocator>, Compaler :: Symbol *>>>>> Compaler :: SymbolTable :: mSymbols 'является частным ../SymbolTable/SymbolTable.cpp:98: ошибка: в этом контексте

Что я делаю не так?

Сводка ответов:

Благодаря Сету Карнеги и Дэниелу Р. Хиксу я понял это. В интересах любого, кто может совершить те же ошибки в будущем, я собираюсь обобщить ответы на мой вопрос, потому что в комментариях было много взад-вперед.

Первая (повторяющийся символ, когда реализация находилась в SymbolTable.h) возникла из-за того, что я пытался одновременно скомпилировать и связать два файла, которые включали SymbolTable.h. Я трудился из-за неправильного понимания того, как #ifdef работают охранники; то есть они будут только препятствовать тому, чтобы код был включен дважды в один файл, но не помогут вам, если вы попытаетесь связать два файла, оба из которых включали код из третьего.

Вторая проблема заключалась в том, что я забыл поместить свою реализацию operator<< в пространство имен Compaler, когда оно было в файле SymbolTable.cpp.

Ответы [ 3 ]

3 голосов
/ 06 ноября 2011

Если вы делаете это в заголовочном файле, вам нужно использовать inline:

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };

    inline ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}

Если вы хотите сделать это в исходном файле.Тогда оператор << должен быть объявлен в пространстве имен </p>

Header.h

namespace Compaler // It's a pun; don't ask
{    
    class SymbolTable
    {
        public:
            friend ostream& operator<<(ostream &out, const SymbolTable &table);
        private:
            vector<map<int, Symbol*> > mSymbols;
    };
    ostream& operator<<(ostream &out, const SymbolTable &table);
}

Source.cpp

#include "Header.h"
namespace Compaler   // must be here
{    
    ostream& operator<<(ostream &out, const SymbolTable &table)
    {
         out << table[0].something;
         return out;
    }
}
2 голосов
/ 06 ноября 2011

Как уже указывалось в других ответах и ​​комментариях, это можно сделать функцией друга.Однако альтернатива, которую я часто предпочитаю, состоит в том, чтобы сделать публичную функцию печати, которая печатает объект в std :: ostream, а затем разрешить оператору << вызвать эту функцию.Это позволяет избежать нарушения инкапсуляции, сделав оператор << функцией друга: </p>

namespace Compaler // It's a pun; don't ask
{

class SymbolTable
{
public:
  // ...
  void print(ostream & out); // basically your definition of operator<<, in your .cpp file
private:
  vector<map<int, Symbol*> > mSymbols;
};

// this doesn't have to be inline, but since it's a two-liner, there's probably no harm either
inline ostream& operator<<(ostream &out, const SymbolTable &table)
{
    table.print(out);
    return out;
}

// ...

}

. Это также дает вашим клиентам возможность избежать синтаксиса <<, что может быть, а может и не быть хорошим. </p>

Еще одна хитрость: если вы делаете это для нескольких классов, вы можете сделать оператор << шаблоном, чтобы любой тип, реализующий print (ostream &), мог выводиться в поток: </p>

template <typename T>
inline ostream& operator<<(ostream &out, const T &obj)
{
    obj.print(out);
    return out;
}

Так какэто шаблонная функция, любые ее конкретные определения для класса будут переопределять это определение, а классы, которые не определяют print (ostream &), являются fne, если они никогда не выводятся в поток, так как эта функция никогда не будет реализованав этом случае.

2 голосов
/ 06 ноября 2011

Первая проблема заключается в том, что вы помещаете определение в заголовочный файл и #include используете его дважды.Это вызывает многократную ошибку определения.

Что касается второй проблемы, возможно, ваша функция имеет другую сигнатуру, чем объявление friend.

Просто чтобы вы знали, как альтернативаправо подписи, вы можете написать встроенную функцию:

class SymbolTable
{
public:
  // ...
  friend ostream& operator<<(ostream &out, const SymbolTable &table) {
      ...
  }
private:
  vector<map<int, Symbol*> > mSymbols;
};

Редактировать: Поскольку, очевидно, подпись верна, ошибка лежит в другом месте.Вам придется опубликовать больше кода.

...