Как перегрузить & lt; & lt; & lt; в std :: stringstream [duplicate]

Моим любимым решением является использование Zend Framework 2. Он также рассматривает свойства $_SERVER HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP, REMOTE_ADDR, но объявляет класс для установки некоторых доверенных прокси и возвращает один IP-адрес, а не массив. Я думаю, что это самое близкое к нему решение:

class RemoteAddress
{
    /**
     * Whether to use proxy addresses or not.
     *
     * As default this setting is disabled - IP address is mostly needed to increase
     * security. HTTP_* are not reliable since can easily be spoofed. It can be enabled
     * just for more flexibility, but if user uses proxy to connect to trusted services
     * it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].
     *
     * @var bool
     */
    protected $useProxy = false;

    /**
     * List of trusted proxy IP addresses
     *
     * @var array
     */
    protected $trustedProxies = array();

    /**
     * HTTP header to introspect for proxies
     *
     * @var string
     */
    protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';

    // [...]

    /**
     * Returns client IP address.
     *
     * @return string IP address.
     */
    public function getIpAddress()
    {
        $ip = $this->getIpAddressFromProxy();
        if ($ip) {
            return $ip;
        }

        // direct IP address
        if (isset($_SERVER['REMOTE_ADDR'])) {
            return $_SERVER['REMOTE_ADDR'];
        }

        return '';
    }

    /**
     * Attempt to get the IP address for a proxied client
     *
     * @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
     * @return false|string
     */
    protected function getIpAddressFromProxy()
    {
        if (!$this->useProxy
            || (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
        ) {
            return false;
        }

        $header = $this->proxyHeader;
        if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {
            return false;
        }

        // Extract IPs
        $ips = explode(',', $_SERVER[$header]);
        // trim, so we can compare against trusted proxies properly
        $ips = array_map('trim', $ips);
        // remove trusted proxy IPs
        $ips = array_diff($ips, $this->trustedProxies);

        // Any left?
        if (empty($ips)) {
            return false;
        }

        // Since we've removed any known, trusted proxy servers, the right-most
        // address represents the first IP we do not know about -- i.e., we do
        // not know if it is a proxy server, or a client. As such, we treat it
        // as the originating IP.
        // @see http://en.wikipedia.org/wiki/X-Forwarded-For
        $ip = array_pop($ips);
        return $ip;
    }

    // [...]
}

Смотрите полный код здесь: https://raw.githubusercontent.com/zendframework/zend-http/master/src /PhpEnvironment/RemoteAddress.php

184
задан Agnel Kurian 24 August 2012 в 17:06
поделиться

5 ответов

Вы заявили свою функцию как friend. Это не член класса. Вы должны удалить Matrix:: из реализации. friend означает, что указанная функция (которая не является членом класса) может обращаться к частным переменным-членам. Способ реализации функции подобен методу экземпляра для класса Matrix, который является неправильным.

106
ответ дан Mehrdad Afshari 18 August 2018 в 10:46
поделиться
  • 1
    И вы должны также объявить его внутри пространства имен Math (не только с использованием пространства имен Math). – David Rodríguez - dribeas 23 March 2009 в 22:35
  • 2
    Почему operator<< должен находиться в пространстве имен Math? Кажется, что это должно быть в глобальном пространстве имен. Я согласен с тем, что мой компилятор хочет, чтобы он находился в пространстве имен Math, но это не имеет смысла для меня. – Mark Lakata 1 May 2015 в 21:58

Предполагая, что мы говорим о перегрузке operator << для всех классов, полученных из std::ostream для обработки класса Matrix (а не для перегрузки << для класса Matrix), имеет смысл объявить функция перегрузки вне пространства имен Math в заголовке.

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

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

Обратите внимание, что перегрузка оператора объявляется вне пространства имен.

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

С другой стороны, если ваша функция перегрузки делает нужно сделать другом, т. е. нуждается в доступе к частным и защищенным членам.

Math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

Вам нужно приложить определение функции блоком пространства имен, а не просто using namespace Math;.

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}
54
ответ дан Dev Null 18 August 2018 в 10:46
поделиться

Просто расскажу вам о другой возможности: мне нравится использовать определения друзей для этого:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

Функция будет автоматически нацелена на окружающее пространство имен Math (хотя его определение появляется в пределах объем этого класса), но не будет отображаться, если вы не вызываете оператор & lt; & lt; с объектом Matrix, который заставит зависящий от аргумента поиск найти это определение оператора. Иногда это может помочь с неоднозначными вызовами, поскольку оно невидимо для типов аргументов, отличных от Matrix. При написании своего определения вы также можете ссылаться непосредственно на имена, определенные в матрице и на матрицу, без определения имени с некоторым возможным длинным префиксом и предоставления параметров шаблона, таких как Math::Matrix<TypeA, N>.

125
ответ дан Johannes Schaub - litb 18 August 2018 в 10:46
поделиться
  • 1
    Спасибо за этот ответ, это было очень полезно для меня. – tommyk 9 December 2010 в 11:41
  • 2
    Я думаю, что этот ответ лучше, чем принятый. Благодаря! – PolyMesh 17 December 2013 в 22:29
  • 3
    Для чего нужен спецификатор друга? – Beginner 23 March 2018 в 08:49

Чтобы добавить к ответу Мехридада,

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

В вашей реализации

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }
61
ответ дан kal 18 August 2018 в 10:46
поделиться
  • 1
    Я не понимаю, почему это голосующий голос, это поясняет, что вы можете объявить оператора в пространстве имен и даже не другом и как вы можете объявить оператора. – kal 24 January 2009 в 21:28
  • 2
    Ответ Mehrdad не имел никакого фрагмента кода, поэтому я просто добавил, что может работать, перемещая его вне класса в самом пространстве имен. – kal 24 January 2009 в 21:30
  • 3
    Я понимаю вашу точку зрения, я только посмотрел на ваш второй фрагмент. Но теперь я вижу, что вы вывели оператора из класса. Спасибо за предложение. – Matthias van der Vlies 24 January 2009 в 21:32
  • 4
    Он не только вне класса, но и правильно определен внутри пространства имен Math. Кроме того, он имеет дополнительное преимущество (возможно, не для матрицы, а для других классов), что «печать» может быть виртуальной и, таким образом, печать будет происходить на самом производном уровне наследования. – David Rodríguez - dribeas 23 March 2009 в 22:45

В C ++ 14 вы можете использовать следующий шаблон для печати любого объекта, который имеет T :: print (std :: ostream & amp;) const; член.

template<class T>
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 
24
ответ дан QuentinUK 18 August 2018 в 10:46
поделиться
  • 1
    На самом деле это, похоже, работает и на C ++ 11. – jotik 12 March 2016 в 15:29
  • 2
    интересное решение! Один вопрос - где должен быть объявлен этот оператор, как в глобальном масштабе? Я предполагаю, что он должен быть виден всем типам, которые можно использовать для его templatize? – barney 16 May 2016 в 06:08
  • 3
    @barney Это может быть в вашем собственном пространстве имен вместе с классами, которые его используют. – QuentinUK 16 May 2016 в 23:30
  • 4
    не можете ли вы просто вернуть std::ostream&, так как это тип возврата в любом случае? – Jean-Michaël Celerier 8 March 2017 в 21:16
  • 5
    @ Jean-MichaëlCelerier При указании типа decltype этот оператор используется только при наличии t :: print. В противном случае он попытается скомпилировать тело функции и дать ошибку компиляции. – QuentinUK 12 March 2017 в 11:43
Другие вопросы по тегам:

Похожие вопросы: