Существует ли законный способ печати кортежей и пар с помощью оператора<<?
У меня есть набор шаблонов/функций, которые позволяют мне печатать кортеж/пару, предполагая, что каждый тип в кортеж/паре имеет operator<<
определенный для него. К сожалению, из-за 17.4.3.1 добавление моих operator<<
перегрузок в std
является незаконным. Есть ли другой способ заставить ADL найти мой operator<<
? Если нет, то есть ли реальный вред в том, чтобы обернуть мою перегрузку в namespace std{}
?
Код для всех заинтересованных: (я использую gcc-4.5)
namespace tuples {
using ::std::tuple;
using ::std::make_tuple;
using ::std::get;
namespace detail {
template< typename...args >
size_t size( tuple<args...> const& )
{
return sizeof...(args);
};
template<size_t N>
struct for_each_ri_impl
{
template<typename Func, typename Tuple>
void operator()(Func func, Tuple const& arg)
{
for_each_ri_impl<N-1>()(func, arg );
func( get<N>( arg ), size(arg) - N - 1 );
}
};
template<>
struct for_each_ri_impl<0>
{
template<typename Func, typename Tuple>
void operator()(Func func, Tuple const& arg)
{
func( get<0>( arg ), size(arg) - 1 );
}
};
}//detail
template<typename Func, typename ... Args>
void for_each_ri( tuple<Args...>const& tup, Func func )
{
detail::for_each_ri_impl< sizeof...(Args)-1>()( func, tup );
}
struct printer {
std::ostream& out;
const std::string& str;
explicit printer( std::ostream& out=std::cout, std::string const& str="," ) : out(out), str(str) { }
template<typename T>void operator()(T const&t, size_t i=-1) const { out<<t; if(i) out<<str; }
};
//Should this next line go into namespace std? Is there another way?
template<typename ... Args>
std::ostream& operator<<(std::ostream& out, std::tuple< Args... > const& tup)
{
out << '[';
tuples::for_each_ri( tup, tuples::printer(out,", ") );
return out << ']';
}
} //tuples
//Edits --
int main()
{
using namespace std;
cout<<make_tuple(1,'a',"Hello")<<endl;
return 0;
}
Компиляция вышеприведенных результатов:
Тест.cpp: в функции 'int main ()':
тест.КПП:69:31: ошибка: не удается выполнить привязку 'с std::поток' lvalue в 'с std::basic_ostream&&' > /опт/местные/включать/gcc45/с++/поток:579:5: ошибка: инициализация аргумента 1 'с std::basic_ostream& СТД::operator&&, const и _Tp&) [с _CharT = типа char, _Traits = с std::char_traits, _Tp = с std::Кортеж]'
2 ответа:
Поместите вокруг него свой собственный класс light wrapper, а затем перегрузите оператор
template< typename ...VA_ARGS > struct format_tuple { typedef tuple<VA_ARGS...> tuple_type; // any format variables const tuple_type & tup; format_tuple( const tuple_type& t): tup(t) {} }; template< typename ...VA_ARGS > format_tuple<VA_ARGS...> makeFormatTuple( const tuple<VA_ARGS...> & t ) { return format_tuple( t ); } template<typename ...VA_ARGS> std::ostream& operator<<( std::ostream& os, const format_tuple<VA_ARGS...> & ft ) { // original implementation }
Это набросок, поскольку я не знаю точно, как это сделать с вариадическими шаблонами, хотя это должно быть возможно. Вы можете легко реализовать несколько версий, хотя с 1, 2, 3 и т.д.параметры, например:
template<typename T1, typename T2, typename T3> class format_tuple_3; //etc template<typename T1, typename T2, typename T3> format_tuple_3<T1, T2, T3> makeFormatTuple( tuple<T1,T2,T3> const&); //etc
Вред заключается в том, что кто-то другой (например, в сторонней библиотеке, которую вы хотите использовать) также добавляет эти объявления в std. Даже если они будут вести себя одинаково, вы нарушите ODR.
Просто поместите их в пространство имен вашего проекта:
namespace kitsune_ymg { // Op<< overloads here. // Your "normal" stuff. void normal_stuff() { std::cout << std::pair<int, int>(42, 3); }
И тогда все в вашем проекте может использовать их.
Я до сих пор точно не знаю, почему это не работает для вас, но, кажется, вы хотите что-то вроде:namespace kitsune_ymg { namespace tuples { // Op<< overloads here. } using namespace tuples; // Your "normal" stuff. } namespace completely_separate_project { using kitsune_ymg::tuples; // Now you can use those op<< overloads in this scope, too. void perfectly_normal_beast() { std::cout << std::pair<int, int>(42, 3); } }