ADL for class in anonymous namespace [closed]

2019-08-01 10:25发布

问题:

Does anybody know why the next piece of code isn't compiled on Clang 4.0.1?

I have the next error:

call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup

There is some file test.cpp

#include <vector>
#include <iostream>


namespace Wrapper
{

template<typename T>
struct QuotedStringImpl
{
    T const& Value;

    explicit QuotedStringImpl(T const& value) :
        Value(value)
    {
    }
};

template<typename T>
inline std::ostream& operator <<(std::ostream& stream, QuotedStringImpl<T> const& rhs)
{
    return stream << rhs.Value;
}

template<>
inline std::ostream& operator <<(std::ostream& stream, QuotedStringImpl<std::string> const& rhs)
{
    return stream << '"' << rhs.Value << '"';
}

template<typename T>
inline QuotedStringImpl<T> QuotedString(T const& value)
{
    return QuotedStringImpl<T>(value);
}

template<typename T>
inline std::ostream& operator <<(std::ostream& stream, std::vector<T> const& value)
{
    stream << "[";
    std::copy(value.begin(), value.end(), std::ostream_iterator<T>(stream, ", "));
    stream << "]";

    return stream;
}

} // namespace Wrapper


namespace
{

struct Struct
{
};

std::ostream& operator<<(std::ostream& stream, Struct const&)
{
    return stream << "(struct value)";
}

} // namespace

int main()
{
    std::vector<Struct> collection(2);
    std::cout << Wrapper::QuotedString(collection);
}

This code is successfully compiled with msvc 15. But I have troubles with Clang 4.0.1. According with this documentation ADL should be applied in place of instantiation. But it doesn't work for me. What is the reason of such behaviour?

回答1:

template<typename T>
inline std::ostream& operator <<(std::ostream& stream, std::vector<T> const& value)
{
    stream << "[";
    std::copy(value.begin(), value.end(), std::ostream_iterator<T>(stream, ", "));
    stream << "]";

    return stream;
}

this operator isn't in any associatied namespace of vector or ostream or T (which in this case is anonymous_ns::Struct). So it cannot be found via ADL.

It is attempted to be called by:

template<typename T>
inline std::ostream& operator <<(std::ostream& stream, QuotedStringImpl<T> const& rhs)
{
    return stream << rhs.Value;
}

which is located earlier in the source.

As a general rule, adding operators outside of the namespace of a type is a bad plan.

As a specific rule, adding operators into namespace std is illegal.

As a consequence, adding operators to types or templates in namespace std is a bad plan.

To fix your specific problem, simply move the definition of the above << above the point where it is called. Your code now compiles. It remains fragile, because it relies on adding operators to types from std.

Live example.

MSVC fails to do proper two-phase lookup, so it (in error) compiles your code. Proper two-phase lookup does a lookup at the point where the template was defined, and then does an ADL only lookup at the point where it is instantiated.

MSVC instead does a full lookup at the point where it is instantiated. This is in violation of the C++ standard.