Как написать оператор << << для boost :: tuple? - PullRequest
5 голосов
/ 29 сентября 2011

В приведенном ниже примере кода показано, что 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 ++ говорит, что они неоднозначны, даже хотя худшее преобразование для первого лучше худшего пересчет за второе:

Ответы [ 3 ]

4 голосов
/ 29 сентября 2011

Проблема не в кортеже, а в вашем операторе.Это прекрасно работает:

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 наследует operator<< от ostream, который имеет эту подпись: ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) разрешено.Затем

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

(замените T всеми доступными типами в c ++, см. Оператор << ссылочная страница </a>

EDIT

Вот упрощенный пример (без кортежа):

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&,...). This also raises another question : why on earth do you need operator << (ostringstream &, ...) `? </p>

3 голосов
/ 29 сентября 2011

Когда вы пишете

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

нет функции, которая соответствует обоим параметрам. Вместо этого у компилятора есть возможность применить неявное преобразование к любому параметру

std::ostream << int

или

std::ostringstream << MyTuple

Последнее произойдет с конструктором boost::tuple, который может принимать любое количество аргументов вплоть до числа элементов кортежа. (И с float это терпит неудачу, потому что float конвертируется в int.)

При перегрузке потоковых операторов используйте базовый класс в качестве левой части (ostream или даже basic_ostream<CharT, Traits>.


Редактировать: Вы можете устранить неоднозначность вызова, приведя аргумент 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& operator<<(ostringstream& os_, Mytuple tuple_)

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

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

1 голос
/ 30 сентября 2011

Все те люди, которые говорят вам использовать ::std::ostream для типа вместо ::std::ostringstream, абсолютно правы.Вы не должны использовать ::std::ostringstream таким образом.

Но моя главная претензия к вашему коду - это печальное отсутствие универсальности.Он работает только для одного конкретного типа кортежа, но не для всех.

Так что я написал operator << для ::std::tuple в C ++ 0x, который работает для любого кортежа, члены которого могут быть написаны индивидуально с использованием 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)".

...