2015-05-05 4 views
3

Я пытаюсь понять, как работают рекурсивные вариационные шаблоны.C++ 11 рекурсивных вариационных шаблонов

#include <iostream> 

template<typename T> 
static inline void WriteLog(T&& msg) { 
    std::wcout << std::forward<T>(msg); 
} 

template<typename T, typename... Ts> 
static void WriteLog(T&& msg, Ts&&... Vals) { 
    WriteLog(std::forward<T>(msg)); 
    WriteLog(std::forward<Ts>(Vals)...); 
    std::wcout << "\n**End**"; 
} 

int main() { 
    WriteLog("apple, ", "orange, ", "mango"); 
} 

Выход:

apple, orange, mango 
**End** 
**End** 

Я ожидал, что только один **End**. Почему он печатается дважды?

+1

У вас есть три параметра и сбрить один в рекурсивном вызове. Почему вы ожидаете, что шаблон многопараметрических функций будет вызван только один раз? – Wintermute

+0

после того, как напечатано 'apple, orange, mango' оно напечатано' ** End ** 'дважды, что меня смущает. В какое время 'std :: wcout <<" \ n ** End ** ";' вызывается дважды подряд. – Praveen

ответ

10

дерево вызовов:

WriteLog("apple, ", "orange, ", "mango"); 
     ->WriteLog("apple, "); 
      -> std::wcout << "apple, "; 
     ->WriteLog("orange, ", "mango"); 
      ->WriteLog("orange, "); 
       -> std::wcout << "orange, "; 
      ->WriteLog("mango"); 
       -> std::wcout << "mango"; 
      ->std::wcout << "\n**End**"; 
     ->std::wcout << "\n**End**"; 
1

**End** распечатывается для звонков

  • WriteLog("apple, ", "orange, ", "mango"); в main
  • WriteLog("orange, ", "mango"); (с линией WriteLog(std::forward<Ts>(Vals)...);) в WriteLog("apple, ", "orange, ", "mango")
3

Когда рекурсивный вызов WriteLog(std::forward<Ts>(Vals)...); завершен, он должен выполнить следующий оператор. Эта функция вызывается дважды (один раз для "apple" и один раз для "orange") и, таким образом, записываются две распечатки "**End**".

Последний рекурсивный вызов для "mango" идет прямо к первой перегрузке, поскольку в пакете остается только один аргумент.

1

Я буду честен с вами, я пишу код C++ 11 шаблонов для 4-х лет, и я до сих пор имеют проблемы с запоминанием, как соответствовать пустой аргумент пакет ...

этот маленький трик избегает рекурсивного расширения шаблона вообще: (EDIT: переписана для поддержки нулевых аргументов и автоматической вставки запятой разделителя)

#include <iostream> 

namespace detail { 
    struct writer 
    { 
     template<class T> 
     void operator()(const T& t) { 
      if (_first) { 
       _first = false; 
      } 
      else { 
       std::cout << ", "; 
      } 
      std::cout << t; 
     } 

    private: 
     bool _first = true; 
    }; 

    // non-template overload to catch no-parameter case 
    void do_write(writer&&) 
    { 

    } 

    // general case. Note w is passed by r-value reference 
    // to allow the caller to construct it in-place   
    template<typename T, typename...Ts> 
    void do_write(writer&& w, const T& t, Ts&&...ts) 
    { 
     w(t); 
     do_write(std::forward<writer>(w), std::forward<Ts>(ts)...); 
    } 


} 

// method 1 - no recursion 
template<typename... Ts> 
void WriteLog1(Ts&&... Vals) { 
    // expand one call for each parameter 
    // use comma operator to ensure expression result is an int 
    detail::writer write; 

    using expander = int[]; 
    expander { 0, (write(std::forward<Ts>(Vals)), 0)... }; 

    // write the final linefeed 
    std::cout << std::endl; 
} 

// method 2 - recursion 

template<typename...Ts> 
void WriteLog2(Ts&&...ts) 
{ 
    detail::do_write(detail::writer(), std::forward<Ts>(ts)...); 
    std::cout << std::endl; 
} 

int main() { 
    WriteLog1("apple", "orange", "mango"); 
    WriteLog1("apple", "orange"); 
    WriteLog1("apple"); 
    WriteLog1("apple", 1.0, "orange", 1L, "mango", 2.6f); 
    WriteLog1(); // test pathalogical case 

    WriteLog2("apple", "orange", "mango"); 
    WriteLog2("apple", "orange"); 
    WriteLog2("apple"); 
    WriteLog2("apple", 1.0, "orange", 1L, "mango", 2.6f); 
    WriteLog2(); // test pathalogical case 
    return 0; 
} 

выход:

apple, orange, mango                                    
apple, orange                                      
apple                                        
apple, 1, orange, 1, mango, 2.6                                 

apple, orange, mango                                    
apple, orange                                      
apple                                        
apple, 1, orange, 1, mango, 2.6 

>                                 
+0

Хорошо пятнистый. Будет редактировать. –

+0

тогда как '(void) x;' является распространенной идиомой для подавления предупреждения, она не самая портативная (см. Http://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/) , – Jarod42

+0

спасибо за ссылку. На самом деле это компиляция без предупреждений с -pedantic: 'using expander = int []; expander {0, (написать (std: forward (Vals)), 0) ...}; ' –

Смежные вопросы