如何为 boost::tuple 编写 `<<` 运算符?

发布于 2024-12-07 07:10:41 字数 1133 浏览 4 评论 0 原文

在下面的示例代码中,它表明可以从第一个模板参数隐式创建 boost::tuple 。 因此,我无法编写 << 运算符,因为它变得不明确。

我也不明白为什么 ostringstream& << float 也是不明确的。这没有任何隐含的构造。为什么这也会给出不明确的错误?

#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <sstream>
#include <string>

using namespace std;

class Myclass
{
};

typedef boost::tuple<int,float,Myclass> Mytuple;

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
  float f = tuple_.get<1>();
  //os_ << (int)tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
  //os_ << tuple_.get<1>();      // No Clue Why this is ambiguous.
  //os_ << tuple_.get<2>();      // Error because no matching operator. Fine.
  return os_;
}

int main()
{
  Mytuple t1;
  t1 = 3;      // Working because int is implicitly converted into Mytuple!! WHY?
  //t1 = 3.0f; // Error because no matching constructor. Fine.
  return 0;
}

错误信息:

tupleTest2.C:18: 错误:ISO C++ 说这些是不明确的,甚至 尽管第一个最差的转换比最差的要好 第二个转换:

In the sample code below, it shows that boost::tuple can be created implicitly from the first template argument.
Because of that I am not able to write a << operator as it becomes ambiguous.

Also I don't understand why ostringstream& << float is also ambiguous. This does not have any implicit construction. Why does this also give ambiguous error?

#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <sstream>
#include <string>

using namespace std;

class Myclass
{
};

typedef boost::tuple<int,float,Myclass> Mytuple;

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
  float f = tuple_.get<1>();
  //os_ << (int)tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
  //os_ << tuple_.get<1>();      // No Clue Why this is ambiguous.
  //os_ << tuple_.get<2>();      // Error because no matching operator. Fine.
  return os_;
}

int main()
{
  Mytuple t1;
  t1 = 3;      // Working because int is implicitly converted into Mytuple!! WHY?
  //t1 = 3.0f; // Error because no matching constructor. Fine.
  return 0;
}

Error Mesasge:

tupleTest2.C:18: error: ISO C++ says that these are ambiguous, even
though the worst conversion for the first is better than the worst
conversion for the second:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你与昨日 2024-12-14 07:10:41

问题不在于元组,而在于您的运算符。这工作正常:

ostream& operator<<(ostream& os_, Mytuple tuple_)
{
    os_ << tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
    os_ << tuple_.get<1>();      // No Clue Why this is ambiguous.
    //os_ << tuple_.get<2>();      // Error because no matching operator. Fine.
    return os_;
}

问题是 ostringstream 从 ostream 继承了 operator<<,它具有以下签名:ostringstream&允许使用运算符<<(ostringstream& os_, Mytuple tuple_)。然后

ostream& operator<<(ostream& os, T t)

(将 T 更改为 c++ 中的所有可用类型,请参阅 operator<< ; 参考页

编辑

这是一个简化的示例(没有元组):

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
    const int i = tuple_.get<0>();
    os_ << i; // error in this line
    return os_;
}

现在错误是:

dfg.cpp: In function ‘std::ostringstream& operator<<(std::ostringstream&, Mytuple)’:
dfg.cpp:18: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ostream.tcc:111: note: candidate 1: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
dfg.cpp:14: note: candidate 2: std::ostringstream& operator<<(std::ostringstream&, Mytuple)

上面的错误消息说:无法在两个运算符之间进行选择operator<<(ostream&,...)operator<<(ostringstream&,...)。这也提出了另一个问题:你到底为什么需要代码>运算符<<(ostringstream&,...)`?

The problem is not with the tuple, but with your operator. This works fine :

ostream& operator<<(ostream& os_, Mytuple tuple_)
{
    os_ << tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
    os_ << tuple_.get<1>();      // No Clue Why this is ambiguous.
    //os_ << tuple_.get<2>();      // Error because no matching operator. Fine.
    return os_;
}

The problem is that the ostringstream inherit operator<< from ostream, which has this signature : ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) is allowed. Then the

ostream& operator<<(ostream& os, T t)

(change T with all available types in c++, see operator<< reference page

EDIT

Here is a simplified example (without a tuple) :

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
    const int i = tuple_.get<0>();
    os_ << i; // error in this line
    return os_;
}

and the error is now :

dfg.cpp: In function ‘std::ostringstream& operator<<(std::ostringstream&, Mytuple)’:
dfg.cpp:18: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ostream.tcc:111: note: candidate 1: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
dfg.cpp:14: note: candidate 2: std::ostringstream& operator<<(std::ostringstream&, Mytuple)

The above error message says : it is not possible to choose between two operators operator<<(ostream&,...) and operator<<(ostringstream&,...). This also raises another question : why on earth do you needoperator<<(ostringstream&,...)`?

爱冒险 2024-12-14 07:10:41

当您编写时,

 os << tuple_.get<0>();

没有与这两个参数匹配的函数。相反,编译器可以选择对任一参数应用隐式转换

std::ostream << int

,或者

std::ostringstream << MyTuple

后者将发生在 boost::tuple 构造函数中,该构造函数可以接受任意数量的参数,最多可达元组元素的数量。 (对于 float 会失败,因为 float 可转换为 int。)

重载流运算符时,使用基类作为左侧(ostream 甚至 basic_ostream


编辑: 您可以通过投射 first 来消除调用的歧义然而

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
  static_cast<std::ostream&>(os_) << tuple_.get<0>(); 
  static_cast<std::ostream&>(os_)  << tuple_.get<1>();      
  static_cast<std::ostream&>(os_)  << tuple_.get<2>();      // Error because no matching operator. Fine.
  return os_;
}

,使用 ostringstream 重载运算符仍然是一个坏主意,因为它无法与运算符链接一起

MyTuple a, b;
ostringstream ss;
ss << a << ' ' << b;

使用:

1) ostringstream&运算符<<(ostringstream&os_, Mytuple tuple_)

2) ostream& ostream::operator<<(char)

3) ostream&运算符<<(ostream&&, boost::tuple

When you write

 os << tuple_.get<0>();

there is no function that matches both parameters. Instead the compiler has a choice to apply an implicit conversion on either parameter

std::ostream << int

or

std::ostringstream << MyTuple

The latter would happen with the boost::tuple constructor that can take any number of arguments up to number of tuple elements. (And with float it fails, because float is convertible to int.)

When overloading stream operators, use the base class as the left hand side (ostream or even basic_ostream<CharT, Traits>.


Edit: You could disambiguate the call by casting the first argument.

ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
  static_cast<std::ostream&>(os_) << tuple_.get<0>(); 
  static_cast<std::ostream&>(os_)  << tuple_.get<1>();      
  static_cast<std::ostream&>(os_)  << tuple_.get<2>();      // Error because no matching operator. Fine.
  return os_;
}

However, overloading the operator with ostringstream is still a bad idea, because it won't work with operator chaining.

MyTuple a, b;
ostringstream ss;
ss << a << ' ' << b;

will invoke:

1) ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)

2) ostream& ostream::operator<<(char)

3) ostream& operator<<(ostream&&, boost::tuple<int,float,Myclass>

抽个烟儿 2024-12-14 07:10:41

所有告诉您使用 ::std::ostream 作为类型而不是 ::std::ostringstream 的人都是绝对正确的。您不应该以这种方式使用 ::std::ostringstream

但我对你的代码的主要不满是令人痛苦地缺乏通用性。它仅适用于一种特定的元组类型,而不适用于所有类型。

因此,我用 C++0x 为 ::std::tuple 编写了一个 operator << ,它适用于任何可以使用 单独编写成员的元组。运算符<<。它可能可以相对容易地转换为与 Boost 的元组类型一起使用。如下所示:

template < ::std::size_t fnum, typename tup_type>
void print_fields(::std::ostream &os, const tup_type &val)
{
   if (fnum < ::std::tuple_size<tup_type>::value) {
      ::std::cerr << "Fred " << fnum << '\n';
      os << ::std::get<fnum, tup_type>(val);
      if (::std::tuple_size<tup_type>::value > (fnum + 1)) {
         os << ", ";
      }
      print_fields<fnum + 1, tup_type>(os, val);
   }
}

template < ::std::size_t fnum, typename... Elements>
class field_printer;

template <typename... Elements>
class field_printer<0, Elements...> {
 public:
   typedef ::std::tuple<Elements...> tup_type;

   static void print_field(::std::ostream &os, const tup_type &val) {
   }
};

template < ::std::size_t fnum, typename... Elements>
class field_printer {
 public:
   typedef ::std::tuple<Elements...> tup_type;

   static void print_field(::std::ostream &os, const tup_type &val) {
      constexpr auto tupsize = ::std::tuple_size<tup_type>::value;
      os << ::std::get<tupsize - fnum, Elements...>(val);
      if (fnum > 1) {
         os << ", ";
      }
      field_printer<fnum - 1, Elements...>::print_field(os, val);
   }
};

template <class... Types>
::std::ostream &operator <<(::std::ostream &os, const ::std::tuple<Types...> &val)
{
   typedef ::std::tuple<Types...> tup_type;
   os << '(';
   field_printer< ::std::tuple_size<tup_type>::value, Types...>::print_field(os, val);
   return os << ')';
}

这会将元组打印为 "(element1, element2, ...elementx)"

All those people telling you to use ::std::ostream for the type instead of ::std::ostringstream are absolutely correct. You shouldn't be using ::std::ostringstream that way.

But my main beef with your code is the distressing lack of generality. It only works for one particular tuple type, and not all of them.

So I wrote an operator << for ::std::tuple in C++0x that works for any tuple who's members can be individually written using operator <<. It can probably be translated relatively easily to work with Boost's tuple type. Here it is:

template < ::std::size_t fnum, typename tup_type>
void print_fields(::std::ostream &os, const tup_type &val)
{
   if (fnum < ::std::tuple_size<tup_type>::value) {
      ::std::cerr << "Fred " << fnum << '\n';
      os << ::std::get<fnum, tup_type>(val);
      if (::std::tuple_size<tup_type>::value > (fnum + 1)) {
         os << ", ";
      }
      print_fields<fnum + 1, tup_type>(os, val);
   }
}

template < ::std::size_t fnum, typename... Elements>
class field_printer;

template <typename... Elements>
class field_printer<0, Elements...> {
 public:
   typedef ::std::tuple<Elements...> tup_type;

   static void print_field(::std::ostream &os, const tup_type &val) {
   }
};

template < ::std::size_t fnum, typename... Elements>
class field_printer {
 public:
   typedef ::std::tuple<Elements...> tup_type;

   static void print_field(::std::ostream &os, const tup_type &val) {
      constexpr auto tupsize = ::std::tuple_size<tup_type>::value;
      os << ::std::get<tupsize - fnum, Elements...>(val);
      if (fnum > 1) {
         os << ", ";
      }
      field_printer<fnum - 1, Elements...>::print_field(os, val);
   }
};

template <class... Types>
::std::ostream &operator <<(::std::ostream &os, const ::std::tuple<Types...> &val)
{
   typedef ::std::tuple<Types...> tup_type;
   os << '(';
   field_printer< ::std::tuple_size<tup_type>::value, Types...>::print_field(os, val);
   return os << ')';
}

This prints out the tuple as "(element1, element2, ...elementx)".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文