为 std::pair<int, int> 重载 operator>>。

9

我正在尝试在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工作,operator>>必须与my_pair在同一个命名空间内,因此必须使用std。这样做会导致未定义的行为,因为我将向命名空间std添加函数。我想避免继承,例如struct my_pair : std::pair。如何解决这个问题?我正在使用OS X上的clang++-3.6。

@KirilKirov:“我想避免继承”。别名不是一种类型,它是一个别名。 - sehe
@sehe - 哦,我没有注意到它是“using”而不是真正的类型。我确实错过了什么 :) - Kiril Kirov
3个回答

7

不需要对值进行ADL-hooking,你可以在流上重载(以某种方式标记它):

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;
    }
}

请查看 Coliru 实时演示,该演示输出如下内容:
std::pair{42, I'm gonna try}
std::pair{1729, science}

2
这样做会导致未定义的行为,因为我将向命名空间 std 添加函数。我想避免继承,例如 struct my_pair : std::pair。我本来想说“继承”,但你不同意...你可以使用封装,只需在 std::pair 上添加另一个强类型即可(但在这种微不足道的情况下,您最好使用自定义结构 - 您的注释代码)。
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更像是一个构建块,而不是用于表示语义信息的东西(也不应该直接打印到流中)。


我认为没有理由不使用继承。my_pair 是一个 std::pair<int,int>,这正是继承的用途。 - Lightness Races in Orbit
@LightnessRacesinOrbit,我觉得唯一需要使用继承的情况就是在OP的问题中:"我想避免使用继承,就像struct my_pair:std::pair。" - utnapistim
幸运的是,我并不感到受到愚蠢和未解释的限制的束缚:这确实是解决这个问题的最佳方法。 :) - Lightness Races in Orbit

2

我知道你说过不想使用继承,但是在it技术中我一定会使用继承:

#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实际上就是一个std::pair<int, int>,你只需要将其作为你自己命名空间中的不同类型即可。这就是继承的用途。

我在这里留下这个例子,以展示它有多容易实现,并解释为什么我认为你应该这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接