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

时间:2019-02-22 05:17:29

标签: c++ c++14 name-lookup

我为operator<<实例定义了一个std::pair输出函数,供一些想要在不注意预期的情况下打印值的单元测试使用。我的测试代码还具有成对的对,这些对作为具有自己的operator<<的另一个类(特别是boost::optional)的成员而保留,但是为了举例说明,我在这里定义了一个简单的Container类代替。问题是operator<<的{​​{1}}值似乎在容器类的std::pair中不可见。

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; } 。这是来自GCC的消息:

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和Apple的Clang 1000.11中,我得到了相同的错误,但措辞却不同。 45.5(Apple LLVM 10.0.0,带有-std=c++14。)

如果我删除-std=c++17模板及其Container周围的匿名名称空间,该错误就会消失。但这并不是真正的解决方案,因为实际上容器是operator<<,它当然在命名空间boost::optional中,而我不能更改它。

我不清楚为什么我的全局boost在名称空间中不可见,因为全局范围应该是不合格查找的搜索路径的一部分。我最好的猜测是,这是因为我的operator<<是一个模板,并且模板似乎不属于初始不合格查找的一部分,因此ADL会启动并找到在其中定义的其他一些operator<<函数operator<<并作为std::中的成员,因此查找在此处停止。候选函数列表(在编译器的错误消息中)似乎与该解释一致。但是尚不清楚当容器不在命名空间中时为什么起作用

有没有一种方法可以在不修改std::ostream类的情况下完成这项工作?


(作为背景:我正在使用Boost.Test库,并编写类似Container的行,其中BOOST_TEST(some_func() == boost::make_optional(std::make_pair('a', 1)))做一些宏/模板魔术来提取表达式的两侧并输出其值如果它们不匹配,则需要定义一个值BOOST_TEST。Boost为operator<<提供了一个值,而我为其中的optional写了一个值,但是调用从前者到后者就是问题所在。)

2 个答案:

答案 0 :(得分:7)

不合格的查询一次上升一个级别,一旦找到内容就停止。它会在匿名名称空间(您正在从中调用的名称空间)中找到一个operator<<,并在那里停止死亡。

请考虑将pairpair本身的元素包装到您自己的命名空间中的包装器中。然后,您可以定义一个operator<<以执行您想要的任何事情,并由ADL对其进行提取。

答案 1 :(得分:2)

  

是否可以在不修改Container类的情况下完成这项工作?

是的。您必须将operator<<放在名称空间中。

DEMO在这里。

搜索运算符<<仅在定义于Related Post中的命名空间container.value中发生。