«Х не является членом Y», хотя Х является другом Y? - PullRequest
2 голосов
/ 26 марта 2012

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

#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;
}

Ответы [ 5 ]

7 голосов
/ 26 марта 2012

В

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

вы говорите компилятору, что есть функция без шаблона

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

для любого типа, с которым вам доводится создавать экземпляр BTree. Но ты никогда предоставить такую ​​функцию. Вы даете определение для члена, но в качестве функции-члена ваш 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 );
    }
}
3 голосов
/ 29 марта 2012

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

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

, где T - это тот же тип, для которого создан шаблон класса btree.(например, для 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>:: префикс, который вы включили (который указывает, к какому классу принадлежит функция / оператор).

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

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

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

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>& (с акцентом на &), так как более эффективно передать ссылку объекту btree, чем копировать все btree (илиповерхностная копия, если вы используете указатели и используете стандартный копировальный -9696 *)
  • , второй параметр также должен быть помечен const, поскольку (предположительно) вы не хотите изменятьbtree объект во время вывода.Помните, что в этом случае вам нужно будет пометить некоторые неизменяемые методы в btree<T> как const, чтобы разрешить его компиляцию.(См. Часто задаваемые вопросы по const правильности )

ПРАВИТЬ несколько раз, чтобы прояснить и убедиться в правильности

1 голос
/ 26 марта 2012

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

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

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

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

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

внутри class mydata.

0 голосов
/ 26 марта 2012

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 голосов
/ 26 марта 2012

Заменить:

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...