匿名命名空间中 class 的 ADL

ADL for class in anonymous namespace

有人知道为什么 the next piece of code 没有在 Clang 4.0.1 上编译吗?

我有下一个错误:

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

有一些文件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);
}

此代码使用 msvc 15 成功编译。但我在使用 Clang 4.0.1 时遇到问题。根据 this 文档,应应用 ADL 代替实例化。但这对我不起作用。这种行为的原因是什么?

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;
}

此运算符不在 vectorostreamT 的任何关联命名空间中(在本例中为 anonymous_ns::Struct)。所以无法通过ADL找到它。

它试图被调用:

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

在源中更早

一般来说,在类型的命名空间之外添加运算符是一个糟糕的计划。

作为特定规则,在namespace std中添加运算符是非法的。

因此,向 namespace std 中的类型或模板添加运算符是一个糟糕的计划。

要解决您的特定问题,只需将上述 << 的定义移到调用它的位置上方即可。您的代码现在可以编译。它仍然很脆弱,因为它依赖于向来自 std.

的类型添加运算符

Live example.

MSVC 无法进行正确的两阶段查找,因此它(错误地)编译了您的代码。正确的两阶段查找在定义模板的位置进行查找,然后在实例化的位置进行 ADL only 查找。

MSVC 而是在它被实例化的地方进行 完整 查找。这违反了 C++ 标准。