2015-03-25 3 views
-1

Я играю с вариативными шаблонами, и в настоящее время я пытаюсь реализовать operator<< для tuple.template looping through tuple

Я пробовал следующий код, но он не компилируется (GCC 4.9 с -std = C++ 11).

template<int I, typename ... Tlist> 
void print(ostream& s, tuple<Tlist...>& t) 
{ 
    s << get<I>(t) << ", "; 
    if(I < sizeof...(Tlist)){ 
     print<I+1>(s,t); 
    } 
} 
template<typename ... Tlist> 
ostream& operator<<(ostream& s, tuple<Tlist...> t) 
{ 
    print<0>(s,t); 
    return s; 
} 

Сообщение об ошибке очень непонятная и долго, но в основном говорит, что нет никакого вызова функции согласования для ГЭТ. Может кто-нибудь объяснить мне, почему? Спасибо.

EDIT: Вот шаблон конкретизации Я использую

auto t = make_tuple(5,6,true,"aaa"); 
cout << t << endl; 
+1

MCVE как обычно пожалуйста! Как вы фактически создаете экземпляр функции шаблона? –

+1

Возможный дубликат [Pretty-print std :: tuple] (http://stackoverflow.com/questions/6245735/pretty-print-stdtuple) – bobah

+0

Большая часть кода, который распаковывает кортежи по позиции, которую я видел, идет от 'sizeof ... (Tlist) 'вниз до' 0', а не наоборот. –

ответ

1

кодекса в if (blah) { блоке } компилируются и должны быть действительными, даже если условие blah ложно.

template<bool b> 
using bool_t = std::integral_constant<bool, b>; 

template<int I, typename ... Tlist> 
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::false_type) { 
    // no more printing 
} 

template<int I, typename ... Tlist> 
void print(std::ostream& s, std::tuple<Tlist...> const& t, std::true_type) { 
    s << std::get<I>(t) << ", "; 
    print<I+1>(s, t, bool_t<((I+1) < sizeof...(Tlist))>{}); 
} 
template<typename ... Tlist> 
std::ostream& operator<<(std::ostream& s, std::tuple<Tlist...> const& t) 
{ 
    print<0>(s,t, bool_t<(0 < sizeof...(Tlist))>{}); 
    return s; 
} 

должно работать. Здесь мы используем диспетчеризацию меток, чтобы контролировать, какую перегрузку мы рекурсивно вызываем: третий аргумент равен true_type, если I является допустимым индексом для кортежа и false_type, если нет. Мы делаем это вместо заявления if. Мы всегда рекурсируем, но когда мы доходим до конца кортежа, мы возвращаемся к завершающей перегрузке.

live example

Как и в сторону, он является неоднозначным, если перегрузка << для двух типов, определенных в std соответствует стандарту: это зависит, если std::tuple<int> является «определенный пользователем тип» или нет, пункт, что стандарт не определяет.

Кроме того, считается передовой практикой перегрузки операторов для типа в пространстве имен этого типа, поэтому его можно найти через ADL. Но перегрузка << внутри std является незаконной по стандарту (вы не можете вводить новые перегрузки в std). Результатом может быть несколько удивительное поведение в некоторых случаях, когда обнаружена неправильная перегрузка или перегрузка не найдена.

0

Вы должны использовать специализацию или SFINAE как ветвь, даже если она не принимается генерирует конкретизации:

template<int I, typename ... Tlist> 
void print(ostream& s, tuple<Tlist...>& t) 
{ 
    s << get<I>(t) << ", "; 
    if(I < sizeof...(Tlist)){ 
     print<I+1>(s,t); // Generated even if I >= sizeof...(Tlist) 
    } 
} 

И поэтому у вас было бы бесконечное создание print, если get<sizeof...(Tlist)> не произведет ошибку раньше.

Вы можете написали без рекурсии:

template<std::size_t ... Is, typename Tuple> 
void print_helper(std::ostream& s, const Tuple& t, std::index_sequence<Is...>) 
{ 
    int dummy[] = { 0, ((s << std::get<Is>(t) << ", "), 0)...}; 
    (void) dummy; // remove warning for unused var 
} 


template<typename Tuple> 
void print(std::ostream& s, const Tuple& t) 
{ 
    print_helper(s, t, std::make_index_sequence<std::tuple_size<Tuple>::value>()); 
} 

Live example