2010-10-27 3 views
2

Я пытаюсь перегрузить оператор + в классе леса, лес представляет собой коллекцию деревьев, а оператор + должен объединить два леса в один. У меня есть следующий код, мое определение класса:Перегрузка + оператор на универсальном классе в C++

template<typename NODETYPE> 
class Forest 
{ 


    public: 

     friend Forest& operator+<>(Forest&, Forest&); 
     friend ostream& operator<<<>(ostream&, const Forest&); 
     friend istream& operator>><>(istream&, Forest&); 
     Forest(); 
     Forest(const Forest& otherForest); 
     ~Forest(); 
     void nodes(int&) const; 

    private: 
     ForestNode<NODETYPE> *root; 

     ForestNode<NODETYPE> *getNewNode(const NODETYPE &); 
}; 

Ниже приводится моя реализация оператора +:

template<typename NODETYPE> 
Forest& operator+<>(Forest& f1, Forest& f2) 
{ 
    f3 = new Forest(); 
    f3.root = *f1.*root; 
    f3.root.sibling = *f2.*root; 
    *f1.root = 0; 
    *f2.root = 0; 
    return f3; 
} 

Я получаю следующее сообщение об ошибке на компиляции:

         
 
|28|error: expected constructor, destructor, or type conversion before '&' token|

линия 28 относится к подписи моего оператора + реализации.

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

template<typename NODETYPE> 
Forest<NODETYPE>& operator+<>(Forest& f1, Forest& f2) 
{ 
    f3 = new Forest(); 
    f3.root = *f1.*root; 
    f3.root.sibling = *f2.*root; 
    *f1.root = 0; 
    *f2.root = 0; 
    return f3; 
} 

Но это дает мне следующие ошибки:

         
 
|28|error: declaration of 'operator+' as non-function| |28|error: missing template arguments before '&' token| |28|error: 'f1' was not declared in this scope| |28|error: missing template arguments before '&' token| |28|error: 'f2' was not declared in this scope|

Может кто-нибудь помочь мне с этим? Я был бы очень благодарен.

+3

Почему вы динамически выделяете, а затем пытаетесь вернуть указатель на «Лес» вместо ссылки? И эта функция не должна возвращать ссылку в любом случае, ссылаться на нее нечего: когда вы добавляете две вещи, вы обычно получаете новое значение *, поэтому возвращайте значение. Это * плохое * дополнение, вы манипулируете аргументами. ('2 + 2' наверняка не изменит' 2'!) Возможно, вам нужна функция 'comb' member. – GManNickG

+0

Выделение нового леса в кучу и возврат ссылки - очень плохая идея. Просто создайте локальный объект и верните его значение. –

+0

Или используйте интеллектуальные указатели, если копирование стоит дорого. –

ответ

5

Ключ к оператору пишущего + является не писать оператор +.. Вместо этого, написать копию CTOR и оператор + =:

template<class NodeType> 
struct Forest { 
    //... 
    Forest(Forest const &other); 
    //... 
    Forest& operator+=(Forest const &other) { 
    // code here 
    return *this; 
    } 
    //... 
}; 

Теперь мы добавим оператор +:

template<class NodeType> 
struct Forest { 
    //... 
    friend Forest operator+(Forest a, Forest const &b) { 
    a += b; 
    return a; 
    } 
    //... 
}; 

И это все! Копирование, как правило, выполняется прямолинейно (иногда, будучи запрещенным), и может быть проще думать в терминах + = + + (у вас есть два объекта и их мутация, а не создание третьего объекта из двух). Этот шаблон для op + работает с любым подобным типом и даже для аналогичных операторов, таких как -, * и /.

+0

Принимая первый параметр по значению вместо ссылки на константу, полезно, поскольку мы все равно его скопируем, но b следует принять за const-reference. – CashCow

+0

@CashCow: Ты абсолютно прав, и это то, что я намеревался; здесь довольно поздно, поэтому, если вы обнаружите больше ошибок, вам придется подождать. :) – 2010-10-27 11:17:35

+0

Примечание для тех, кто проходит мимо, может возникнуть соблазн сделать это 'return a + = b', но этого достаточно, чтобы обмануть компилятор от обработки результата' a + = b' так же, как 'a', поэтому NRVO не применяется (и в C++ 0x он не перемещается.) Конечно, вы можете возвращать std :: move (a + = b); ', чтобы помочь, но вы на самом деле не набираете что-нибудь. – GManNickG

2

Вы должны предоставить аргументы шаблона для всех параметров Forest.

template<typename NODETYPE> 
Forest<NODETYPE> operator+(Forest<NODETYPE>& f1, Forest<NODETYPE>& f2) 

Кроме того, рассмотреть вопрос о внесении аргументов const ссылки убедиться, что вы не манипулировать ими.

Есть severalquestions о stackoverflow относительно шаблонов функций друзей. У C++ FAQ также есть страница, в которой объясняются некоторые основы.

+2

И удалите '<>'. Однако его код плохой, поэтому он не может делать ссылки на const. – GManNickG

+0

Ах, спасибо. Я бы сделал это, но требования к проекту состоят в том, что аргументы будут удалены впоследствии по какой-то причине. – joedillian

+0

@joedillian: Это плохо. Перегрузка оператора - это обоюдоострый меч, поскольку он часто приводит к коду, который трудно понять, если операторы ведут себя неинтуитивно. Никто не ожидает '+' для удаления операндов. –

0

Вы можете определить operator+ шаблон следующим образом:

template< class NodeType > 
Forest<NodeType> operator+(Forest<NodeType> const& f1, Forest<NodeType> const& f2) 
{ 
    // Implementation. 
} 

Приветствия & НТН,

3

Перегрузка оператора может быть хорошей или плохой. Хорошо, когда это приводит к упрощению просмотра кода. Плохо, когда это приводит к тому, что писатели либо перегружают неправильной семантикой (но это решение, которое компилируется), либо когда интуитивный способ использования оператора приводит к крайне неэффективному коду.

Обратите внимание, что последний оператор может применяться и к std :: string, что потенциально может сделать большое количество копий, и именно поэтому в стандарте C++ 03 указано, что строка не должна храниться внутри в непрерывном буфера (в прежние времена они использовали ссылки копирования на запись и могли хранить такие ссылки для обеих строк, которые были объединены до тех пор, пока это не понадобится. Впоследствии было обнаружено, что он не является потоковым и делает его более дорогостоящим, чем просто копирование буфера теперь они копируют каждый раз и снова неэффективны).

(Обратите внимание, что стандарт C++ 11, который распознает проблемы с потоками и атомами, гарантирует, что базовое значение должно быть смежным и завершающим нуль, чтобы сделать операции чтения безопасными).

Правильная подпись оператора + (в том случае, все тот же тип) выглядит следующим образом:

T operator+(const T&, const T&); 

В функции члена было бы:

class T 
{ 
    // make public if necessary 
    T operator+(const T& rhs) const; 
}; 

Вы можете реализовать оператор + автоматически в качестве шаблона, когда оператор + = доступен с

template<typename T, typename R> 
T operator+(const T& lhs, const R& rhs) 
{ 
    T copy(lhs); 
    return copy += rhs; 
} 

Если вы хотите dec перегрузите оператора вашего шаблона в качестве друга, это правильный способ сделать это. Я покажу его с оператором < <

// first some forward declarations, assume ostream already declared with #include <iosfwd> minimum 
template< typename T > class Forest; 
template< typename T > std::ostream & operator<<(std::ostream & os, const Forest<T> & for); 

template< typename T> class Forest 
{ 
    friend std::ostream& operator<< <>(std::ostream&, const Forest<T> &); 
    //rest of class Forest 
}; 

template< typename T > 
std::ostream & operator<<(std::ostream& os, const Forest<T> & forest) 
{ 
    // implement 
    return os; 
} 

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

  1. развязно объявить класс как template
  2. Проложить объявить метод как функцию шаблона
  3. Сделать функцию другом с помощью <> перед открывающимися круглыми скобками, обозначающими параметры
  4. Внедрите функцию после своего класса.
+0

Функции друга для op <<, определенные в определении класса, намного проще. – 2010-10-28 00:04:22

Смежные вопросы