std::experimental::ostream_joiner 和 std::pair

arg*_*t6x 5 c++ operator-overloading c++17

在 c++17/g++7 中,终于出现了久违的 ostream_joiner。它可以正确输出到 ostreams,使用中缀分隔符分隔集合元素。

#include <algorithm>
#include <experimental/iterator>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>

using string = std::string;
#if 1
struct pair {
    string first;
    string second;
};
#else
using pair = std::pair<string,string>;
#endif


std::ostream& operator<<(std::ostream& lhs, const pair &p) {
    return lhs << p.first << "=" << p.second;
}

int main()
{
    std::vector<pair> pairs = {{"foo", "bar"}, {"baz", "42"}};
    std::copy(std::begin(pairs),
          std::end(pairs),
          std::experimental::make_ostream_joiner(std::cout, ", "));
}
Run Code Online (Sandbox Code Playgroud)

虽然代码片段成功编译并输出......

foo=bar, baz=42
Run Code Online (Sandbox Code Playgroud)

...在代码段中将 #if 1 更改为 #if 0 会使编译器抱怨缺少正确的移位运算符:

main.cpp:29:70:   required from here
/usr/local/include/c++/7.2.0/experimental/iterator:88:10: error: no match for 
'operator<<' (operand types are 
'std::experimental::fundamentals_v2::ostream_joiner<const char*, char, 
std::char_traits<char> >::ostream_type {aka std::basic_ostream<char>}' and 
'const std::pair<std::__cxx11::basic_string<char>, 
std::__cxx11::basic_string<char> >')
  *_M_out << __value;
Run Code Online (Sandbox Code Playgroud)

有人知道为什么吗?

更新

巴里对这个问题给出了正确的答案。然而它并没有解决问题,并且运行手动循环并不是重用现有的 stl 代码,因此问题扩展为:

是否可以在不污染 std 命名空间的情况下使流操作符工作?

Bar*_*rry 2

在 的实现内部的某个地方ostream_joiner,将会尝试执行以下操作:

os << value;
Run Code Online (Sandbox Code Playgroud)

其中osastd::basic_ostream和 value 是您的pair类型。为了确定对该operator<<调用执行的操作,我们查找在此模板定义点可见的所有重载operator<<()以及参数的关联命名空间中的重载(这称为参数相关查找)。

当您使用your struct pair时,关联的命名空间pair::,因此 ADL 会找到您的::operator<<(std::ostream&, pair const&). 这种超负荷工作,被选择,一切都很高兴。

当你使用std::pair,关联的命名空间pairstd,并且找不到operator<<()带有std::pair. 因此出现了错误。


您可以在自己的命名空间中创建自己的类型,您可以为其添加一个重载的operator<<类型,这可以完全是您自己的类型(问题中的方式),或者您可以继承以下类型std

struct pair : std::pair<string,string> {
    using std::pair<string,string>::pair;
};
std::ostream& operator<<(std::ostream&, my_pair const& ) {...}
Run Code Online (Sandbox Code Playgroud)

或者,您也可以不使用make_ostream_joiner. 可以替换这个:

std::copy(std::begin(pairs),
      std::end(pairs),
      std::experimental::make_ostream_joiner(std::cout, ", "));
Run Code Online (Sandbox Code Playgroud)

有了这个:

const char* delim = "";
for (auto const& pair : pairs) {
    std::cout << delim << pair; // now, our point of definition does include
                                // our operator<<() declaration, we don't need ADL
    delim = ", ";
}
Run Code Online (Sandbox Code Playgroud)