重载运算符>>用于 std::p air<int, int>

Overload operator>> for std::pair<int, int>

本文关键字:gt int lt air std 运算符 用于 重载      更新时间:2023-10-16

我正在尝试在std::pair<int, int>上使用boost::lexical_cast

#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>
namespace my
{
  // When my_pair is a user defined type, this program compiles
  // and runs without any problems.
  // When declaring my_pair as an alias of std::pair<int, int>,
  // it fails to compile
  /*
  struct my_pair
  {
      int first;
      int second;
  };
  */
  using my_pair = std::pair<int, int>;
  std::istream& operator>>(std::istream& stream, my_pair& pair)
  {
    stream >> pair.first;
    stream >> std::skipws;
    stream >> pair.second;
    return stream;
  }
}
int main()
{
  my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
  std::cout << p.first << " " << p.second << std::endl;
  return 0;
}

如果我理解正确的话,为了使ADL工作,操作符>>必须与my_pair在同一个命名空间中,所以std.

这样做将导致未定义的行为,因为我将向命名空间std添加函数。

我想避免继承,如struct my_pair : std::pair<int, int>

这个问题的解决方案是什么?

我在OS x上使用clang++-3.6

您可以重载流(以某种方式标记它),而不是将值绑定到流:

int main() {
    std::map<int, std::string> standback { { 42, "I'm gonna try" }, { 1729, "science" } };
    streaming::tag_ostream out = std::cout;
    for (auto& entry : standback)
        out << entry << "n";
}

这样,您就可以在您控制的名称空间上使用ADL-hook。您可以使标记更通用(如auto out = streaming::tag(std::cout))。

现在,它的一个简单实现看起来像

namespace streaming {
    template <typename T>
    struct tag : std::reference_wrapper<T> {
        using std::reference_wrapper<T>::reference_wrapper;
    };
    using tag_ostream = tag<std::ostream>;
    template <typename T1, typename T2>
    static inline tag_ostream operator<<(tag_ostream os, std::pair<T1, T2> const& p) {
        os.get() << "std::pair{" << p.first << ", " << p.second  << "}";
        return os;
    }
    template <typename Other>
    static inline tag_ostream operator<<(tag_ostream os, Other const& o) {
        os.get() << o;
        return os;
    }
}

参见Live On Coliru,它打印:

std::pair{42, I'm gonna try}
std::pair{1729, science}

我知道你说过你不希望这样,但我肯定使用inheritance:

#include <iostream>
#include <utility>
#include <boost/lexical_cast.hpp>
namespace my
{
   struct my_pair : std::pair<int, int> {};
   std::istream& operator>>(std::istream& stream, my_pair& pair)
   {
      stream >> pair.first;
      stream >> std::skipws;
      stream >> pair.second;
      return stream;
   }
}
int main()
{
    my::my_pair p = boost::lexical_cast<my::my_pair>("10 10");
    std::cout << p.first << " " << p.second << std::endl;
}

(现场演示)

你的my::my_pair字面上是-a std::pair<int, int>;您只需要在自己的名称空间中使用不同的类型即可。这就是继承的作用。

我把它留在这里只是为了展示它是多么容易做到,并解释为什么我认为你应该这样做。

这样做将导致未定义的行为,因为我将向命名空间std添加函数。

我希望避免继承,如struct my_pair: std::pair。

我想说"继承",但你忽略了它…

您可以使用封装,只需在std::pair<int,int>上添加另一个强类型(但在这种微不足道的情况下,您可能更适合使用自定义结构-您的注释代码):

struct my_pair
{
    std::pair<int,int> value;
    // TODO: add any access interface here
};
std::istream& operator>>(std::istream& stream, my_pair& pair)
{
    stream >> pair.value.first;
    stream >> std::skipws;
    stream >> pair.value.second;
    return stream;
}

事实上,您可能应该这样做,因为std::pair更像是一个构建块,而不是应该用来表示语义信息的东西(也不是应该直接打印到流中的东西)。