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

时间:2017-09-27 18:17:56

标签: c++ operator-overloading c++17

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

#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, ", "));
}

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

foo=bar, baz=42

...将#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;

有人知道为什么?

更新

巴里已经给出了正确的答案。然而,它并没有解决问题,并且运行手动循环并不意味着重用现有的stl代码,因此问题扩展到:

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

2 个答案:

答案 0 :(得分:2)

ostream_joiner的实现中的某个地方,会尝试类似:

os << value;

其中osstd::basic_ostream,值是您的pair类型。为了确定对operator<<调用做什么,我们查找在此模板定义点可见的所有重载operator<<()以及关联命名空间中的重载(这被称为argument-dependent lookup)。

当您使用 struct pair时,pair的关联命名空间为::,因此ADL会找到您的::operator<<(std::ostream&, pair const&)。这种超载工作,选择,一切都很快乐。

当您使用std::pair时,pair的关联命名空间为std,并且找不到operator<<()的{​​{1}}。因此错误。

您可以在自己的命名空间中创建自己的类型,您可以为其添加重载std::pair,这可以完全属于您自己的类型(问题中的方式),也可以继承operator<<

std

或者,您可以不使用struct pair : std::pair<string,string> { using std::pair<string,string>::pair; }; std::ostream& operator<<(std::ostream&, my_pair const& ) {...} 。可以取代这个:

make_ostream_joiner

用这个:

std::copy(std::begin(pairs),
      std::end(pairs),
      std::experimental::make_ostream_joiner(std::cout, ", "));

答案 1 :(得分:0)

  

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

如果您愿意将copy替换为transform,然后愿意-将put_invocation取自my answer here,请使用:

std::transform(
    std::begin(pairs), std::end(pairs),
    std::experimental::make_ostream_joiner(std::cout, ", "),
    [](auto& p) {
        return put_invocation([&p = p](auto& os) {
            // adl works just fine here
            return os << p;
            // or `os << p.first << "=" << p.second;`
        });
    }
);

这应该优化为与注入到std名称空间中的版本完全相同的代码。