为什么命名空间中的函数看不到全局定义的运算符<<?

Why can't a function in a namespace see my operator<< defined globally?

本文关键字:lt 运算符 定义 全局 命名空间 函数 看不到 为什么      更新时间:2023-10-16

我已经为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命名空间中进行。相关文章。