首页 > 解决方案 > 为什么命名空间中的函数看不到我的 operator<< 全局定义?

问题描述

我已经operator<<为实例定义了一个输出函数std::pair,供一些单元测试使用,如果他们没有看到预期的值,他们想要打印这些值。我的测试代码也有对,它们作为另一个类的成员持有operator<<——特别是boost::optional,但为了举例,我在Container这里定义了一个简单的类。问题是operator<<for值在容器类中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;
}

输出普通对的末端附近的行工作正常。但是当试图在容器中输出对时,编译器找不到operator<<for 对。这是来自 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不在列表中。

(这条消息来自 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,当然它在 namespaceboost中,我无法改变它。

我不清楚为什么我的全局operator<<在命名空间中不可见,因为全局范围应该是不合格查找的搜索路径的一部分。我最好的猜测是,这是因为 myoperator<<是一个模板,并且模板似乎不是最初的非限定查找的一部分,所以 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内部编写了一个,但是从前者到后者的调用是问题所在。)

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

解决方案


不合格查找一次上升一级,并在找到某些内容后立即停止。它operator<<会在匿名命名空间中找到一个 - 正是您从中调用的命名空间 - 并停在那里。

pair考虑将 of或pair自身的元素包装到您自己的命名空间中的包装器中。然后你可以定义一个operator<<做任何你想做的事情,并让 ADL 接受它。


推荐阅读