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& &l开发者_运维技巧t;< 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:
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 need
operator<<(ostringstream&,...)`?
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>
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)"
.
精彩评论