为什么命名空间中的函数看不到我全局定义的 operator<<?
Why can't a function in a namespace see my operator<< defined globally?
我已经为 std::pair
个实例定义了一个 operator<<
输出函数,以供某些单元测试使用,如果它们没有观察到预期的值,则它们想要打印值。我的测试代码也有作为另一个 class 的成员持有的对,它有自己的 operator<<
— 特别是 boost::optional
,但为了举例,我定义了一个简单的 [=16] =] class 这里代替。问题是 std::pair
值的 operator<<
在容器 class.
的 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<<
。这是来自 GCC 的消息:
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
模板不在列表中。
(此消息来自带有 -std=c++14
的 Debian 的 GCC 6.3.0。我从带有 -std=c++14
的 Debian 的 Clang 3.8.1-24 和 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
class 的情况下完成这项工作?
(作为背景:我正在使用 Boost.Test 库并编写类似 BOOST_TEST(some_func() == boost::make_optional(std::make_pair('a', 1)))
的行,其中 BOOST_TEST
做了一些 macro/template 魔术来提取表达式并在它们不匹配时输出它们的值。这要求值定义 operator<<
。Boost 为 optional
提供了一个,我已经为 std::pair
编写了一个其中,但前者对后者的调用是问题所在。)
Is there a way to make this work without modifying the Container class?
是的。您必须将 operator<<
放在命名空间中。
DEMO 这里
搜索运算符 <<
仅在 container.value
定义的命名空间内发生。Related Post.
不合格查找一次上一级,一找到就停止。它会在匿名命名空间中找到一个 operator<<
- 就是你调用的那个 - 并在那里停止。
考虑将 pair
的元素或 pair
本身包装到您自己的命名空间中的包装器中。然后你可以定义一个 operator<<
来做任何你想做的事情并让 ADL 拾取它。
我已经为 std::pair
个实例定义了一个 operator<<
输出函数,以供某些单元测试使用,如果它们没有观察到预期的值,则它们想要打印值。我的测试代码也有作为另一个 class 的成员持有的对,它有自己的 operator<<
— 特别是 boost::optional
,但为了举例,我定义了一个简单的 [=16] =] class 这里代替。问题是 std::pair
值的 operator<<
在容器 class.
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<<
。这是来自 GCC 的消息:
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
模板不在列表中。
(此消息来自带有 -std=c++14
的 Debian 的 GCC 6.3.0。我从带有 -std=c++14
的 Debian 的 Clang 3.8.1-24 和 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
class 的情况下完成这项工作?
(作为背景:我正在使用 Boost.Test 库并编写类似 BOOST_TEST(some_func() == boost::make_optional(std::make_pair('a', 1)))
的行,其中 BOOST_TEST
做了一些 macro/template 魔术来提取表达式并在它们不匹配时输出它们的值。这要求值定义 operator<<
。Boost 为 optional
提供了一个,我已经为 std::pair
编写了一个其中,但前者对后者的调用是问题所在。)
Is there a way to make this work without modifying the Container class?
是的。您必须将 operator<<
放在命名空间中。
DEMO 这里
搜索运算符 <<
仅在 container.value
定义的命名空间内发生。Related Post.
不合格查找一次上一级,一找到就停止。它会在匿名命名空间中找到一个 operator<<
- 就是你调用的那个 - 并在那里停止。
考虑将 pair
的元素或 pair
本身包装到您自己的命名空间中的包装器中。然后你可以定义一个 operator<<
来做任何你想做的事情并让 ADL 拾取它。