Не могу понять что не нравится clang`у

146
19 января 2019, 18:40

Visual studio 19.00.23506 и g++ 5.4.0 нормально компилируют код приведенный ниже, но вот clang 3.8.0 дает ошибку. Не пойму, что ему не нравится и как надо переписать код, чтобы "этот" его понял.

Код:

#include <boost/optional.hpp>
#include <sstream>
#include <iostream>
template<class T>
std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value)
{
    if (value) {
        stream << *value;
    } else {
        stream << "none";
    }
    return stream;
}
namespace XXX 
{
struct A
{
    int x = 1;
};
struct B
{
    A a;
};
} // XXX

std::wostream& operator<<(std::wostream& stream, const XXX::A& a)
{
    stream << "x=" << a.x;
    return stream;
}
std::wostream& operator<<(std::wostream& stream, const XXX::B& b)
{
    stream << "a=("<< b.a <<")";
    return stream;
}

int main()
{
    boost::optional<XXX::B> b;
    std::wcout << b;    
}

Код в online компиляторе тут.

Лог компилятора:

Error(s):
source_file.cpp:10:10: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
                stream << *value;
                       ^
source_file.cpp:47:16: note: in instantiation of function template specialization 'operator<<<XXX::B>' requested here
    std::wcout << b;    
               ^
source_file.cpp:37:16: note: 'operator<<' should be declared prior to the call site or in namespace 'XXX'
std::wostream& operator<<(std::wostream& stream, const XXX::B& b)
               ^
1 error generated.
Answer 1

У вас operator << вызывается из шаблона функции

template<class T>
std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value)

в контексте, который является зависимым, т.е. зависит от шаблонного параметра T.

Такие вызовы разрешаются через две фазы поиска имен:

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

    В вашем примере оттуда не находится ничего подходящего, ибо над определением шаблона у вас ничего подходящего не объявлено.

  2. ADL поиск (argument-dependent lookup), который делается из контекста вызова вашей шаблонной функции (в main). Из этого места могут быть видны дополнительные объявления, но при этом эти дополнительные объявления ищутся только в ассоциированных пространствах имен (ассоциированных с аргументами вашего вызова).

    В вашем примере ассоциированными пространствами имен являются std и XXX. В них в контексте вызова ничего нового и подходящего не видно. Глобальное пространство имен в вашем случае ассоциированным не является, поэтому то, что вы там дополнительно наобъявляли, найдено через ADL не будет.

Ваши глобальные операторы

std::wostream& operator<<(std::wostream& stream, const XXX::A& a);
std::wostream& operator<<(std::wostream& stream, const XXX::B& b);

не находятся, код не компилируется.

Либо объявляйте ваши дополнительные операторы выше объявления шаблона функции, чтобы они находились на шаге 1. Либо делайте их членами XXX, чтобы они находились на шаге 2 (при этом их можно оставить "внизу").

Второй вариант логичнее. С чего это вы вообще решили захламить глобальное пространство имени этими функциями, которые к нему не имеют никакого отношения?

#include <boost/optional.hpp>
#include <sstream>
#include <iostream>
template<class T>
std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value)
{
    if (value) {
        stream << *value;
    } else {
        stream << "none";
    }
    return stream;
}
namespace XXX 
{
    struct A
    {
        int x = 1;
    };
    struct B
    {
        A a;
    };
    std::wostream& operator<<(std::wostream& stream, const A& a)
    {
        stream << "x=" << a.x;
        return stream;
    }
    std::wostream& operator<<(std::wostream& stream, const B& b)
    {
        stream << "a=("<< b.a <<")";
        return stream;
    }
} // XXX
int main()
{
    boost::optional<XXX::B> b;
    std::wcout << b;    
}

Не закрытым остается только вопрос о том, почему GCC вдруг соглашался компилировать вашу оригинальную версию кода. Я минимизировал пример и задал соответствующий вопрос на EnSO

GCC and ADL for operators in expressions

Оказывается, это известный баг GCC (bug 51577, а также родственный bug 70099), который проявляется именно при вызове перегруженных операторов через обычный операторный синтаксис. То есть Clang здесь совершенно прав, отвергая вашу версию кода.

VC же вообще не реализует какого-то вменяемого "двухфазного" поиска имен, а просто без разбора видит все подряд, что видно из точки вызова в main.

Answer 2

Попробуйте вот так:

#include <boost/optional.hpp>
#include <sstream>
#include <iostream>
namespace XXX 
{
struct A
{
    int x = 1;
};
struct B
{
    A a;
};
} // XXX

std::wostream& operator<<(std::wostream& stream, const XXX::A& a)
{
    stream << "x=" << a.x;
    return stream;
}
std::wostream& operator<<(std::wostream& stream, const XXX::B& b)
{
    stream << "a=("<< b.a <<")";
    return stream;
}
template<class T>
std::wostream& operator<<(std::wostream& stream, const boost::optional<T>& value)
{
    if (value) {
        stream << *value;
    } else {
        stream << "none";
    }
    return stream;
}
int main()
{
    boost::optional<XXX::B> b;
    std::wcout << b;    
}

За подробностями лучше сходить сюда: https://clang.llvm.org/compatibility.html Там прямо примеры со всеми ошибками.

READ ALSO
Проверка числа на целочисленность С++

Проверка числа на целочисленность С++

Пишу программу на статистику, и мне необходимо посчитать квартилиПроблема состоит в том что всё время проходит установка else, и непонятно,...

398
Conan + CMake C++

Conan + CMake C++

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

143
статический виртуальный метод в C++ - возможно ли

статический виртуальный метод в C++ - возможно ли

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

144