2015-11-05 153 views
0

我發現幾乎令人滿意的解決方案我的問題here(第二回答),但我不能用這種方式寫在另一個編譯單元中的代碼,因爲把代碼放在頭文件中導致鏈接器抱怨多個函數定義和只將聲明放在頭文件中會導致鏈接器未定義的引用問題。可變模板遞歸類型通過

這裏是我的代碼:

template <typename... types> 
void foo(); 

template<> 
void foo<>() { 
    return; 
} 

template<typename type, typename... types> 
void foohelper() { 
    foo<types...>(); 
} 

template <typename... types> 
void foo() { 
    foohelper<types...>(); 
} 

int main() { 
    foo<int, int>(); 
} 

這裏是我想達到的目標:

class A { 
public: 
    template<> 
    void foo<>() { 
     return; 
    } 

    template<typename parser, typename... parsers> 
    void foohelper() { 
     foo<parsers...>(); 
    } 

    template <typename... parsers> 
    void foo() { 
     foohelper<parsers...>(); 
    } 
}; 

int main() { 
    A a; 
    a.foo<int, int>(); 
} 

但是在編譯過程中這將導致以下錯誤:

explicit specialization 'void A::foo(void)' is not a specialization of a function template 

有什麼這個簡單的解決方案?

+0

你嘗試移動一般(非專業)'foo'聲明上述專業之一,而類定義後,將所有定義? – Petr

回答

2

不需要遞歸。這是簡單的:

#include <iostream> 
#include <string> 
#include <typeinfo> 

class A { 
public: 

    template<typename parser> 
    void foohelper() { 
     std::cout << "handled a " << typeid(parser).name() << std::endl; 
     // do work here 
    } 

    template <typename... parsers> 
    void foo() { 
     using expand = int[]; 
     (void) expand { 0, (foohelper<parsers>(), 0)... }; 
    } 
}; 

int main() { 
    A a; 
    a.foo<int, int, double, std::string>(); 
} 

輸出樣本:

handled a i 
handled a i 
handled a d 
handled a NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE 

編輯:

爲響應不符合微軟的編譯器的要求,這裏是另一種不依賴於未施膠陣列上的版本:

#include <iostream> 
#include <string> 
#include <typeinfo> 

class A { 
public: 

    template<typename parser> 
    void foohelper() { 
     std::cout << "handled a " << typeid(parser).name() << std::endl; 
     // do work here 
    } 

    template <typename... parsers> 
    void foo() { 
     // c++ strictly does not allow 0-sized arrays. 
     // so here we add a NOP just in case parsers is an empty type list 
     using expand = int[1 + sizeof...(parsers)]; 
     (void) expand { 
      (foohelper<void>(), 0), 
      (foohelper<parsers>(), 0)... 
     }; 
    } 

}; 

// implement the NOP operation. Note specialisation is outside class definition. 
template<> void 
A::foohelper<void>() {} 



int main() { 
    A a; 
    a.foo<int, int, double, std::string>(); 
    a.foo<>(); 
} 

編輯2:

具有前綴,後綴和分析器間調用的更完整示例。寫了這麼多的代碼後,你可能會開始思考,「嘿!我可以在這裏實現一個完整的領域特定語言!」,你會是對的。

然而,比這更復雜的可能會讓你對你的同事產生永恆的仇恨,所以我會避免沿着這條路線走下去。

#include <iostream> 
#include <string> 
#include <typeinfo> 

class A { 
public: 

    template<typename parser> 
    void foohelper() { 
     std::cout << "handled a " << typeid(parser).name(); 
     // do work here 
    } 

    void prepare() 
    { 
     std::cout << "starting parsers: "; 
    } 

    void separator() 
    { 
     std::cout << ", "; 
    } 

    void nothing() 
    { 

    } 

    void done() { 
     std::cout << " done!" << std::endl; 
    } 

    template <typename... parsers> 
    void foo() { 
     // c++ strictly does not allow 0-sized arrays. 
     // so here we add a NOP just in case parsers is an empty type list 
     bool between = false; 
     using expand = int[2 + sizeof...(parsers)]; 
     (void) expand { 
      (prepare(), 0), 
      ((between ? separator() : nothing()), between = true, foohelper<parsers>(), 0)..., 
      (done(), 0) 
     }; 
    } 

}; 


int main() { 
    A a; 
    a.foo<int, int, double, std::string>(); 
    a.foo<>(); 
} 

輸出樣本:

starting parsers: handled a i, handled a i, handled a d, handled a NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE done! 
starting parsers: done! 
+0

完美!它甚至可以用arm-none-eabi編譯器編譯。 – omicronns

+0

在使用VisualStudio 2013(平臺工具集v120)進行編譯時,我收到了一條警告,在使用更復雜的程序而不僅僅是代碼段時會導致崩潰。警告: 「警告C4789:大小爲4字節的緩衝區'將被溢出; 4字節將從偏移量4開始寫入」 警告與模板參數一樣多,偏移量遞增4字節。 – omicronns

+0

秒,給我一點時間爲你的編譯器做一個解決方法。微軟編譯器總是有點微妙:) –