我已经为std::pair
实例定义了一个operator<<
输出函数,供一些单元测试使用,这些单元测试希望在不监视预期内容时打印值。 我的测试代码也有对,这些对作为另一个类的成员持有,该类有自己的operator<<
- 特别是boost::optional
,但为了示例,我在这里定义了一个简单的Container
类。 问题是std::pair
值的operator<<
在容器类的operator<<
中似乎不可见。
#include <iostream>
#include <utility>
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &out, std::pair<T1, T2> const &pair) {
return out << "{ " << pair.first << ", " << pair.second << " }";
}
namespace {
template <typename T>
struct Container {
T value;
};
template <typename T>
std::ostream &operator<<(std::ostream &out, Container<T> const &container) {
return out << container.value; // Error!
}
}
int main() {
std::pair<char, int> pair { 'a', 1 };
Container<std::pair<char, int>> container { pair };
std::cout << pair << std::endl;
std::cout << container << std::endl;
}
输出普通对的末端附近的行工作正常。 但是,当尝试在容器中输出对时,编译器找不到对的operator<<
。 以下是来自海湾合作委员会的消息:
test.cc: In instantiation of ‘std::ostream& {anonymous}::operator<<(std::ostream&, const {anonymous}::Container<T>&) [with T = std::pair<char, int>; std::ostream = std::basic_ostream<char>]’:
test.cc:28:16: required from here
test.cc:18:16: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘const std::pair<char, int>’)
return out << container.value;
~~~~^~~~~~~~~~~~~~~~~~
。然后是一长串考虑的所有候选函数operator<<
,以及为什么每个函数都不适合(因为它们都适用于不同类型的值)。 我的std::pair
模板不在列表中。
(此消息来自 Debian 的 GCC 6.3.0-std=c++14
。 我从 Debian 的 Clang 3.8.1-24 和带有-std=c++14
的 Apple Clang 1000.11.45.5(Apple LLVM 10.0.0)和-std=c++17
中得到相同的错误,但措辞不同。
如果我删除Container
模板及其operator<<
周围的匿名命名空间,错误就会消失。 但这不是真正的解决方案,因为实际上容器是boost::optional
的,这当然是在命名空间boost
中,我无法更改它。
我不清楚为什么我的全局operator<<
在命名空间内不可见,因为全局范围应该是非限定查找的搜索路径的一部分。 我最好的猜测是,这是因为我的operator<<
是一个模板,而模板似乎不是初始非限定查找的一部分,因此 ADL 启动并找到一堆其他operator<<
函数在std::
中定义并作为std::ostream
中的成员,所以查找停止到此。 候选函数的列表(在编译器的错误消息中)似乎与该解释一致。 但是,目前还不清楚为什么当容器不在命名空间中时它确实有效。
有没有办法在不修改Container
类的情况下完成这项工作?
(作为背景:我正在使用 Boost.Test 库并编写像BOOST_TEST(some_func() == boost::make_optional(std::make_pair('a', 1)))
这样的行,其中BOOST_TEST
做一些宏/模板魔术来提取表达式的两侧,如果它们不匹配,则输出它们的值。 这要求值具有定义operator<<
。 Boost 为optional
提供了一个,我已经为其中的std::pair
写了一个,但从前者到后者的调用是问题所在。
非限定查找一次上升一个级别,一旦找到某些内容就会停止。它在匿名命名空间(您从中调用的命名空间)中找到一个operator<<
,并在那里停止。
请考虑将pair
元素或pair
本身包装到您自己的命名空间中的包装器中。然后,您可以定义一个operator<<
来执行任何您想要的操作,并让 ADL 选取它。
有没有办法在不修改容器类的情况下完成这项工作?
是的。您必须将operator<<
放在命名空间中。
在这里演示。
搜索运算符<<
仅在定义container.value
命名空间中进行。相关文章。