匿名名称空间中类的ADL

时间:2018-10-03 14:52:31

标签: c++ templates operator-overloading argument-dependent-lookup

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

下一个错误:

  

调用函数'operator <<',该函数在模板中都不可见   定义,也不通过与参数相关的查找找到

有一些文件 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感到麻烦。根据{{​​3}}文档,应使用ADL代替实例化。但这对我不起作用。这种行为的原因是什么?

1 个答案:

答案 0 :(得分: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;
}

此运算符不在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 查找。

MSVC会在实例化点进行 full 查找。这违反了C ++标准。