Clang/ G ++ разница с функцией друга

Почему код ниже хорошо скомпилирован в g ++, но выдает ошибку на clang?

#include <iostream>

class Object {};

class Print
{
public:
    template <typename CharT>
    inline friend std::basic_ostream<CharT> & operator<<(std::basic_ostream<CharT> & out, const Object&)
    {
        return (out << "object");
    }
    static void f( const Object& str )
    {
        std::cout << str;
    }
};

int main()
{
    std::cout << Object() << std::endl;
    return 0;
}

Проверочные ссылки: g ++ / clang ++

Когда я переместил функцию Friend в глобальное пространство имен, код хорошо скомпилирован для обоих компиляторов ( clang ++ / g ++).

Какая реализация в этом случае более совместима с C++ Standart?

2 ответа

Решение

Clang здесь прав. Функции-друзья, определенные внутри классов, могут быть найдены только с помощью поиска по аргументам по их аргументам, а не обычным поиском. Так как Print не связана с областью Object, operator<< не должен быть найден. Частичная цитата из Стандарта:

7.3.1.2 Определения членов пространства имен [namespace.memdef]

3 Каждое имя, впервые объявленное в пространстве имен, является членом этого пространства имен. Если объявление друга в нелокальном классе сначала объявляет класс, функцию, шаблон класса или шаблон функции 97, то друг является членом самого внутреннего окружающего пространства имен. Объявление друга само по себе не делает имя видимым для неквалифицированного поиска (3.4.1) или квалифицированного поиска (3.4.3). [Примечание: имя друга будет отображаться в его пространстве имен, если в области имен пространства предоставляется соответствующее объявление (до или после определения класса, предоставляющего дружбу). - примечание конца] Если вызывается функция или шаблон функции друга, ее имя может быть найдено с помощью поиска по имени, который рассматривает функции из пространств имен и классов, связанных с типами аргументов функции (3.4.2).

Как @sehe упоминает, правильный способ добавить operator<< в Object чтобы определить его как глобальную функцию (используя Object интерфейс) или как friend функция внутри себя (используя private функции от Object), а не в каком-то вспомогательном классе. Смотрите также старую колонку GotW Херба Саттера "Что в классе?"

Непростая задача - объявить статический оператор (друг) в другом классе (который не связан).

  1. Вы можете просто создать его в окружающей области, есть

    • без разницы семантически
    • нет причин иметь его в друзья (как Printer никак не используется)
    • Вы все еще можете сделать его другом, если вам нужно

    Жить на Колиру

  2. В качестве альтернативы, связать другой класс;

    Есть много способов связать пространства имен (§3.4.2) с типом для поиска функции. Например, этот хак будет достаточно, чтобы связать Print пространство имен класса с Object типа так ADL все равно будет работать:

    template <typename> struct Object_ {};
    typedef Object_<class Print> Object;
    

    Смотрите это в прямом эфире на Coliru, а также

Другие вопросы по тегам