Почему эта << не компилируется - PullRequest
2 голосов
/ 22 января 2010

Я не могу понять, почему следующий код не компилируется. Синтаксис такой же, как и у других моих операторов. Есть ли ограничение, что << перегрузка должна быть дружественной? Если так, то почему? Спасибо за любую помощь. </p>

Это не работает -

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

class Test
{
 public:
explicit Test(int var):
    m_Var(var)
    {   }

    std::ostream& operator<< (std::ostream& stream)
    {
        return stream << m_Var;
    }
 private:
int m_Var;

 };

 int _tmain(int argc, _TCHAR* argv[])
 {
Test temp(5);

std::cout << temp;

return 0;
}

Это работает -

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>

class Test
{
public:
explicit Test(int var):
    m_Var(var)
    {   }

    friend std::ostream& operator<< (std::ostream& stream, Test& temp);

private:
    int m_Var;

 };

 std::ostream& operator<< (std::ostream& stream, Test& temp)
 {
return stream << temp.m_Var;
 };

 int _tmain(int argc, _TCHAR* argv[])
 {
Test temp(5);

std::cout << temp;

return 0;
 }

Ответы [ 6 ]

9 голосов
/ 22 января 2010

Это не ограничение, что любой оператор должен быть "friend ed". Проблема в том, что если вы объявляете оператор как метод экземпляра, первый аргумент всегда должен быть типом самого класса. В этом случае вам нужно, чтобы первый параметр оператора (левая сторона) был типа std::ostream&, поэтому вы не можете использовать метод экземпляра для его перегрузки и должны будете использовать глобальную функцию.

Кстати, совсем не обязательно, чтобы операторы, объявленные как отдельные функции, объявлялись также как friend функциями. Они могут счастливо работать, не будучи friend в классе, если они только получают доступ к public членам своих аргументов.

6 голосов
/ 22 января 2010

Потому что первая форма перегружается temp << std::cout.

4 голосов
/ 23 января 2010

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

Возьмите этот код:

   struct Gizmo
    {
        ostream& operator<<(ostream& os) const
        {
            os << 42;
        }
    };


    int main()
    {
        Gizmo g;
        cout << g;
        return 0;
    }

Рассмотрим контекст вызова cout << g; Когда компилятор компилирует эту функцию, он сначала пытается это:

cout.operator<<(g);

... и если он не найден, он ищет в глобальном пространстве имен:

operator<<(cout, g);

... и если он не найден, он не может быть скомпилирован.

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

g.operator<<(cout);

... что он не может сделать, если вы не измените свой код на:

g << cout;

... что, очевидно, не то, что вы собираетесь.

3 голосов
/ 22 января 2010

Когда вы делаете std::cout << temp;, это означает, что вы применяете оператор << к std::cout (так как операторы остаются ассоциативными). Если вы хотите написать оператор, который является функцией-членом для достижения этой цели, вам придется перегрузить оператор << к тому классу, к которому принадлежит std::cout, что невозможно, поскольку это то, что вы не можете изменить.

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

std::ostream& operator<< (std::ostream& stream, Test& temp)

Теперь вы можете сделать это другом или нет. Если вы не сделаете его другом, вам придется предоставить функции получения (например, getMVar), которые предоставят вам значение членов Test class. Однако это не очень хороший подход. Так как это вам не нужно, чтобы обеспечить функции получения. Поэтому обычно принято дружить с такими операторами.

Как указывалось ранее, то, что вы делаете, приведет к тому, что код будет написан как temp << std::cout, что явно не то, что вы хотите.

3 голосов
/ 22 января 2010

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

Использование оператора друга является коротким, лаконичным и может предотвратить нежелательные неявные преобразования (поскольку они используются только через ADL). Если вы хотите определить его вне строки (например, в файле .cpp реализации), пусть он вызывает непубличный и, возможно, виртуальный метод:

struct T {
  template<class Ch, class Tr>
  friend
  std::basic_ostream<Ch, Tr>& operator<<(
    std::basic_ostream<Ch, Tr>& s,
    T const& v
  ) {
    s << v.stuff; // or call v.output(s), etc.
    return s;
  }

private:
  int stuff = 0; // initialization here is c++0x only

  //virtual void output(std::ostream&) const;
  //virtual void output(std::wostream&) const;
  // etc., or make it a template, but if it's short,
  // just put it in the above friend overload
};

Бонусные баллы: Назовите участников перегрузки оператора, которые не имеют this . (Подсказка: они статичны.)

0 голосов
/ 22 января 2010

Как обобщено Скоттом Мейерсом в Effective C ++, 2-е издание Элемент 19: Различать функции-члены, функции, не являющиеся членами, и функции-друзья operator >> и operator << никогда не являются участниками. Если f является оператором >> или оператором <<, сделайте функцию, не являющуюся членом. Если, кроме того, f нужен доступ к закрытым членам C, сделайте f другом C </p> Это утверждение является лишь руководством для принятия решения о том, следует ли создавать элементы operator << и operator >>. Лучше всего сделать их не членами. Вы можете сделать их членами, если хотите, но если вы это сделаете, вы будете вынуждены написать:

temp << std::cout // when calling operator<<
temp >> std::cin  // when calling operator>>

Вы могли бы на самом деле исправить свой первый кусок кода, изменив вызов std :: cout на вышеприведенную форму. Но такой способ написания определенно не естественен.

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

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