2012-03-26 16 views
2

Я пытаюсь написать двоичное дерево. Почему следующий код сообщает об ошибке C2039, «< <»: не является членом «btree < T>», хотя оператор < < был объявлен как функция друга в классе btree?«X не является членом Y», хотя X является другом Y?

#include<iostream> 
using namespace std; 

template<class T> 
class btree 
{ 
public: 
    friend ostream& operator<<(ostream &,T); 
}; 

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 
+3

http://sscce.org/ (особенно ** простой **). – Griwes

+0

Код выглядит длинным, но проблема в классе btree и перегруженной << операторной функции, которая сразу же следует за классом. – user1232138

+0

@OP, каждый раз, когда я вижу такую ​​высокую полосу прокрутки, это указывает на неправильную тестовую запись. Удалите все, что необходимо, и тогда это будет «Простой, самосохраненный правильный пример». – Griwes

ответ

3

По объявляющему друга оператора, компилятору искать функцию

ostream& operator<<(ostream &,T); 

где T точно такой же тип шаблон класса ВТКЕЕ инстанцируется. (Например, для btree<Node>, фактическая подпись будет ostream& operator<<(ostream &, Node); - если вы зайца членов i и n типа Node)

Эта функция будет иметь доступ к частным и защищенным членам (переменные и функции) класса btree<T> для всех случаев от T, но на самом деле он не является членом класса (как и без ключевого слова friend).

Оператор определение вы предоставляете для оператора, который является членом класса шаблона BTree, как если у вас есть объявили

template<class T> 
class btree 
{ 
public: 
    ostream& operator<<(ostream &,T); 
}; 

Это связано с btree<T>:: префиксом включен (т.е. определяет класс, к которому принадлежит функция/operator).

Поскольку в этом классе нет соответствующего оператора декларации (см. Вышеприведенное описание объявления друга), компилятор жалуется.

Чтобы исправить это, вы либо

  • держать друг декларацию, удалите btree<T>:: префикс и template<class T> от оператора Defintion и изменить второй тип параметра в btree<Type>&, где Type является одним из типы, которые вы ожидаете создать экземпляр btree-шаблона (например, Node), - тогда поставьте аналогичные дефекты для других подобных типов.
  • или удалить friend ключевое слово из декларации в классе и удалить параметр T как из декларации и определения как теперь оператор должен работать на всем BTree (который неявно поставляются через *this).
  • В качестве альтернативы, вы можете поэкспериментировать с объявляя друг оператор в качестве шаблона, но это требует еще несколько модификаций: (подробнее о forward declaration)

template<class T> btree; // forward declaration of class btree 

// forward declare operator (or move definition here) 
template<class T> 
ostream& operator<<(ostream &o, btree<T>& s); 

// declare operator as template friend 
template<class T>    
class btree    
{    
public:    
    friend ostream& operator<< <> (ostream &, bree<T>&); 
    // note <> after operator name to denote template with no new template parameters 
}; 

Обратите внимание, что выше я предположил, что вы хотите вывести все дерево (то есть вызывать operator<< на объект btree). Из кода, который у вас есть, неясно, является ли это вашим намерением (класс btree не имеет членов i и n). Если нет и тип, который вы хотите вызвать оператора <<, то это фактический параметр шаблона btree, тогда вам не нужно менять второй параметр шаблонизированного оператора с T, но также нет необходимости объявлять это как friend класса btree, так как оператор не зависит от btree. Вам необходимо объявить его как друга класса, чьи члены i и n у вас есть доступ к определенному оператору определенному (например, Узел выше), если i и/или n являются частными в этом классе. Понятие о потере btree<T>:: (или Node::) по-прежнему применяется, поскольку оператор не принадлежит ни одному классу.

Пара больше вещей, если вы идете с другом объявлении:

  • Тип второго параметра оператора должен быть btree<T>& (акцент на &), поскольку это является более эффективным, чтобы передать reference к btree, чем для копирования всего btree (или мелкой копии, если вы используете указатели и переходите по умолчанию copy-contructor)
  • второй параметр также должен быть помечен как const, так как (предположительно) вы не хотите изменять объект btree во время вывод. Имейте в виду, что в этом случае вам нужно будет отметить некоторые не меняющиеся методы в btree<T> как const, а также разрешить его компиляцию. (См справку по const correctness)

EDIT'd несколько раз, чтобы понять и обеспечить корректность

1

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

Вот и все ключевое слово friend, чтобы предоставить этот доступ к нечленам.

Поскольку ваш номер operator<< не использует btree<T>, нет причин, чтобы сделать его другом btree<T>.

Так что я думаю, что вы имели в виду

friend ostream& operator<<(ostream &, const mydata&); 

внутри class mydata.

+0

но что в этом плохого ??? – user1232138

+1

@ user1232138 Вы объявляете друга без шаблонов, и вы определяете член шаблона с неправильным количеством параметров. –

+0

Вы почти достигли отметки ... см. Мой ответ –

0

Заменить:

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

с:

ostream& operator<<(ostream &o, const mydata &s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

Как отмечает Бен Войт, ошибка говорит вам, что эта функция не членом btree. Кроме того, вы, кажется, определяете эту функцию для mydata исключительно, так как она ожидает s и i пользователей.

+0

все еще не работает. – user1232138

+1

Поскольку для этого оператора требуется доступ к частным членам, он должен быть «другом» класса mydata. –

7

В

template <typename T> 
class BTree 
{ 
    // ... 
    friend std::ostream& operator<<(std::ostream&, T); 
    // ... 
}; 

вы сообщаете компилятору, что существует не свободный шаблон функции

std::ostream& operator<<(std::ostream&, Type) 

для любого типа вы случайно инстанцирует ВТКЕЕ над. Но вы никогда не предоставляете такую ​​функцию . Предоставленное вами определение относится к члену, , но в качестве функции-члена ваш operator<< принимает слишком много параметров.

Учитывая, что BTree является общим типом, он не должен предоставлять средства с отображением его содержащихся элементов; это до содержащегося элемента типа. Что бы иметь смысл что-то вроде:

template <typename T> 
class BTree 
{ 
    struct Node 
    { 
     // ... 
     void display(std::ostream& dest, int indent) const; 
    }; 

    // ... 
    void display(std::ostream& dest) const; 
    friend std::ostream& operator<<(std::ostream& dest, BTree const& tree) 
    { 
     tree.display(dest); 
     return dest; 
    } 
}; 

template <typename T> 
void BTree::display(std::ostream& dest) const 
{ 
    if (myRoot == NULL) { 
     dest << "empty"; 
    } else { 
     myRoot->display(dest, 0); 
    } 
} 

template <typename T> 
void BTree::Node::display(std::ostream& dest, int indent) const 
{ 
    dest << std::string(indent, ' ') << data; 
    if (myLeft != NULL) { 
     myLeft->display(dest, indent + 2); 
    } 
    if (myRight != NULL) { 
     myRight->display(dest, indent + 2); 
    } 
} 
+0

Хотя я согласен с вашими чувствами, мне интересно, почему вы подняли 3 и меня на 2, когда я предложил, по сути, одно и то же. : - \ –

+0

@OrgnlDave: есть больше ответа, чем правильного. Ваш ответ был путаным, и я был не прав (хотя теперь я вижу, что это был просто плохой выбор слова). К сожалению, эта путаница означает, что это плохой ответ. Исправьте его, и, возможно, они будут удалены. –

+0

@MooingDuck У вас есть предложения по его устранению? –

0

EDIT

Поскольку это функция друга, C++ является немного странным. См., Функции друга на самом деле не определены в классе, они определены в другом пространстве имен. Вы должны предоставить функцию вывода, используемую operator<< внутри определения класса, если вы хотите использовать шаблонную функцию-член.

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

94% времени вы можете использовать умный взломать, как это было предложено в комментариях, но это не отвечает на фундаментальный вопрос: ваша функция «друга» не является членом вашего класса, если его тело дается в декларации, периоде, больше ничего не говорится по этому вопросу.

(Что другой 6% времени, вы спросите, что это не нормально? Конструктор копирования нонсенс, CLI и другие отвратительные бугорки в ночное время.)

Если вы абсолютно необходимо включить его пределами , в качестве функции-члена вы бы сделали что-то вроде ...

template<class T> 
class btree 
{ 
    private: 
    int i; 
    int n; 
    void stream_out(std::ostream& o); 

    public: 
    friend std::ostream& operator<<(std::ostream& o, btree<T> & me) { 
     me.stream_out(o); 
     return o; 
    } 
}; 

template <class T> 
void btree<T>::stream_out(std::ostream& o) 
{ 
    o << i << '\t' << n; 
} 

EDITED, чтобы прояснить сводку.

+0

по-прежнему та же ошибка ... – user1232138

+0

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

+0

@ user1232138 Найден и (плохо) объяснил ответ! Функции друзей странны. См. Ответ. –

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