2016-02-17 58 views
1

如果已在其他地方回答了此問題,表示歉意,我確實搜索過,但找不到明確的匹配項。C++ 11可變參數模板類中的可變參數函數不能按預期方式工作

我在模板類中有一個可變參數函數,它不能像我期望的那樣工作。我有一個解決方法,但我懷疑這不是最好的解決方案。

考慮下面的代碼:

#include <iostream> 
#include <functional> 
#include <vector> 

template< typename... ARGS > 
class Kitten 
{ 
public: 
    using Callback = std::function< void(ARGS&&...) >; 

    Kitten() = default; 

    void addCallback(Callback && c) 
    { 
     callbacks.push_back(std::forward<Callback>(c)); 
    } 

    void processCallbacks(ARGS &&... args) 
    { 
     for (Callback const &c : callbacks) 
     { 
      c(std::forward<ARGS>(args)...); 
     } 
    } 

private: 
    std::vector<Callback> callbacks; 
}; 

int main(int argc, const char * argv[]) 
{ 
    (void) argc; 
    (void) argv; 

    Kitten<int, float> kitty; 
    kitty.addCallback([](int i, float f) 
    { 
     std::cout << "Int: " << i << "\nFloat: " << f << "\n"; 
    }); 
    kitty.processCallbacks(2, 3.141f); 

    int ii = 54; 
    float ff = 2.13f; 
    kitty.processCallbacks(ii, ff); 

    return 0; 
} 

這不會編譯,第二次調用processCallbacks會產生一個錯誤(鐺,看到VCl 4類似的問題)。

我可以修復編譯和得到的東西,如果我改變processCallbacks到定義爲預期工作:

template< typename... FORCEIT > 
void processCallbacks(FORCEIT &&... args) 
{ 
    for (Callback const &c : callbacks) 
    { 
     c(std::forward<ARGS>(args)...); 
    } 
} 

在我看來是有點俗氣的解決辦法,即使它似乎工作,我懷疑我錯過了一個更好的解決方案。

我對第一個示例失敗原因的理解是因爲沒有對參數包進行類型推導,所以編譯器不會爲所有情況生成正確的代碼。第二個示例的工作原理是因爲它強制參數包中的類型扣除。

這一直困擾着我一段時間。任何幫助非常感謝。

編輯:VC12編譯器錯誤:

error C2664: 'void Kitten<int,float>::processCallbacks(int &&,float &&)' : cannot convert argument 1 from 'int' to 'int &&' 

編輯:蘋果LLVM版本7.0.0編譯器錯誤:

error: rvalue reference to type 'int' cannot bind to lvalue of type 'int' 

關於變化的意見建議使用std::moveaddCallback似乎在形式上更加靈活:

template< typename FUNCTION > 
void addCallback(FUNCTION && c) 
{ 
    callbacks.emplace_back(std::forward<FUNCTION>(c)); 
} 

使用std::forward,因爲該功能現在需要通用參考。 由於這將允許以下工作:

std::function< void(int, float)> banana([](int i, float f) 
{ 
    std::cout << "Int: " << i << "\nFloat: " << f << "\n"; 
}); 
kitty.addCallback(banana); 
+2

你應該發佈你的編譯器錯誤。 – TartanLlama

+1

'std :: forward < Callback >(c)'等於'std :: move(c)' –

+0

大概我還應該使用'emplace_back'來提高效率? – Slartibartfast

回答

3
void processCallbacks(ARGS &&... args) 
{ 
    //... 
} 

對於ARGS每種類型的允許值類別將模板參數設置爲Kitten。例如。對於Kitten<float, int&, const bool&>processCallbacks將接受第一個參數的右值,第二個值(由於reference collapsing rules)以及第三個值(因爲右值可以綁定到const左值引用)。這不會給你完美的轉發。

template< typename... FORCEIT > 
void processCallbacks(FORCEIT &&... args) 
{ 
    //... 
} 

該函數模板接受左值和右值,因爲有類型推導。這種參數被稱爲轉發參考參數。轉發參考參數的格式必須是T&&,其中T是一些推導出的模板參數。

如果你想接受左值和右值並完善它們,你應該使用後一種形式。儘管起初你可能看起來很奇怪,但這是一個相當常見的模式。

相關問題