匿名命名空间中 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;
}
此运算符不在 vector
或 ostream
或 T
的任何关联命名空间中(在本例中为 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
.
的类型添加运算符
MSVC 无法进行正确的两阶段查找,因此它(错误地)编译了您的代码。正确的两阶段查找在定义模板的位置进行查找,然后在实例化的位置进行 ADL only 查找。
MSVC 而是在它被实例化的地方进行 完整 查找。这违反了 C++ 标准。
有人知道为什么 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;
}
此运算符不在 vector
或 ostream
或 T
的任何关联命名空间中(在本例中为 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
.
MSVC 无法进行正确的两阶段查找,因此它(错误地)编译了您的代码。正确的两阶段查找在定义模板的位置进行查找,然后在实例化的位置进行 ADL only 查找。
MSVC 而是在它被实例化的地方进行 完整 查找。这违反了 C++ 标准。